Самый эффективный способ показать покадровую анимацию андроида

Я пытаюсь показать анимацию кадра за кадром, меняя изображения в образе просмотра изображений. Я попробовал анимацию в xml, а также изменил растровое изображение изображения внутри обработчика. Я также пытался хранить только три растровых изображения внутри arraylist (чтобы избежать нехватки памяти) в качестве механизма кеширования, но на самом деле малое улучшение. Мне нужно повторить 36 изображений для полной анимации. Проблема, с которой я сталкиваюсь, заключается в том, что во всех методах, которые я использовал, я не могу завершить анимацию в заданные временные рамки 50 мс. Изображения варьируются от 250 кбайт до 540 кб. Fps анимации действительно низкий. Когда версия приложения ios готова, я вынужден показать анимацию, совместимую с версией ios. Я noob в renderscript и opengl. Есть ли способ показать гладкую анимацию для больших изображений в 50-60 мс. Любые намеки или предложения заслуживают высокой оценки. Heres моментальный снимок анимации: Здесь ссылка к моим изображениям для любого запрошенного. enter image description here

Ответы

Ответ 1

Я написал простую деятельность, которая делает самую основную вещь:

Загружает все растровые изображения в потоке, затем отправляет изменения в ImageView каждые 40 мс.

package mk.testanimation;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;
import java.util.ArrayList;

public class MainActivity extends Activity {
    ImageView mImageView;

    private int mImageRes[] = new int[]{
        R.drawable.s0,
        R.drawable.s1,
        R.drawable.s2,
        R.drawable.s3,
        R.drawable.s4,
        R.drawable.s5,
        R.drawable.s6,
        R.drawable.s7,
        R.drawable.s8,
        R.drawable.s9,
        R.drawable.s10,
        R.drawable.s11,
        R.drawable.s12,
        R.drawable.s13,
        R.drawable.s14,
        R.drawable.s15,
        R.drawable.s16,
        R.drawable.s17,
        R.drawable.s18,
        R.drawable.s19,
        R.drawable.s20,
        R.drawable.s21,
        R.drawable.s22,
        R.drawable.s23,
        R.drawable.s24,
        R.drawable.s25,
        R.drawable.s26,
        R.drawable.s27,
        R.drawable.s28,
        R.drawable.s29,
        R.drawable.s30,
        R.drawable.s31,
        R.drawable.s32,
        R.drawable.s33,
        R.drawable.s34,
        R.drawable.s35,
    };

    private ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(mImageRes.length);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Handler handler = new Handler();
        mImageView = new ImageView(this);
        setContentView(mImageView);
        Thread important = new Thread() {
            @Override
            public void run() {
                long timestamp = System.currentTimeMillis();
                for (int i = 0; i < mImageRes.length; i++) {
                    mBitmaps.add(BitmapFactory.decodeResource(getResources(), mImageRes[i]));
                }
                Log.d("ANIM-TAG", "Loading all bitmaps took " + (System.currentTimeMillis() - timestamp) + "ms");
                for (int i = 0; i < mBitmaps.size(); i++) {
                    final int idx = i;
                    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            mImageView.setImageBitmap(mBitmaps.get(idx));
                        }
                    }, i * 40);
                }
            }
        };
        important.setPriority(Thread.MAX_PRIORITY);
        important.start();
    }
}

Это выглядело довольно прилично на моем Nexus 7, но для загрузки всех растровых изображений потребовалось немного больше 4 секунд.

Можете ли вы загружать растровые изображения заранее?

Кроме того, он не будет экономить тонну, но ваши png имеют кучу прокладок вокруг прозрачного пространства. Вы можете обрезать их и немного уменьшить память. В противном случае сжатие изображений также поможет (например, ограничение количества используемых цветов).

В идеале, в приведенном выше решении, вы должны переработать растровые изображения сразу же после того, как они больше не будут использоваться.

Кроме того, если это слишком тяжело для памяти, вы можете сделать то, что вы упомянули, и иметь битмарт-буфер, но я уверен, что это должно быть больше трех изображений.

Удачи.

РЕДАКТИРОВАТЬ: Попытка 2. Во-первых, я обрезал все изображения до 590x590. Это бритая около 1 мб изображений. Затем я создал новый класс, который немного "занят" и не имеет фиксированной частоты кадров, но отображает изображения, как только они будут готовы:

package mk.testanimation;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;

import java.util.ArrayList;

public class MainActivity extends Activity {
    ImageView mImageView;

    private int mImageRes[] = new int[]{R.drawable.s0, R.drawable.s1, R.drawable.s2, R.drawable.s3, R.drawable.s4, R.drawable.s5, R.drawable.s6, R.drawable.s7, R.drawable.s8, R.drawable.s9, R.drawable.s10, R.drawable.s11, R.drawable.s12, R.drawable.s13, R.drawable.s14, R.drawable.s15, R.drawable.s16, R.drawable.s17, R.drawable.s18, R.drawable.s19, R.drawable.s20, R.drawable.s21, R.drawable.s22, R.drawable.s23, R.drawable.s24, R.drawable.s25, R.drawable.s26, R.drawable.s27, R.drawable.s28, R.drawable.s29, R.drawable.s30, R.drawable.s31, R.drawable.s32, R.drawable.s33, R.drawable.s34, R.drawable.s35};

    private ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(mImageRes.length);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final long timestamp = System.currentTimeMillis();
        final Handler handler = new Handler();
        mImageView = new ImageView(this);
        setContentView(mImageView);
        Thread important = new Thread() {
            @Override
            public void run() {
                for (int i = 0; i < mImageRes.length; i++) {
                    mBitmaps.add(BitmapFactory.decodeResource(getResources(), mImageRes[i]));
                }
            }
        };
        important.setPriority(Thread.MAX_PRIORITY);
        important.start();
        Thread drawing = new Thread() {
            @Override
            public void run() {
                int i = 0;
                while (i < mImageRes.length) {
                    if (i >= mBitmaps.size()) {
                        Thread.yield();
                    } else {
                        final Bitmap bitmap = mBitmaps.get(i);
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                mImageView.setImageBitmap(bitmap);
                            }
                        });
                        i++;
                    }
                }
                Log.d("ANIM-TAG", "Time to render all frames:" + (System.currentTimeMillis() - timestamp) + "ms");
            }
        };
        drawing.setPriority(Thread.MAX_PRIORITY);
        drawing.start();
    }
}

Вышеуказанное начало рендеринга начинаться почти сразу, и на моем 2012 Nexus 7 потребовалось меньше 4 секунд.

В качестве последнего усилия я преобразовал все изображения в 8-разрядные PNG вместо 32-разрядных. Это привело к рендерингу менее 2 секунд!

Я готов поспорить, что любое решение, в котором вы закончите, поможет сделать изображения как можно меньше.

Снова - удача!

Ответ 2

У меня проблема с той же проблемой, и я решил ее, переопределив AnimationDrawable. Итак, если проблема заключается в том, что вы не можете загружать все изображения в массив, потому что они слишком велики для хранения памяти, а затем загружают изображение, когда вам это нужно. My AnimationDrawable:

public abstract class MyAnimationDrawable extends AnimationDrawable {

    private Context context;
    private int current;
    private int reqWidth;
    private int reqHeight;
    private int totalTime;

    public MyAnimationDrawable(Context context, int reqWidth, int reqHeight) {
        this.context = context;
        this.current = 0;
        //In my case size of screen to scale Drawable
        this.reqWidth = reqWidth;
        this.reqHeight = reqHeight;
        this.totalTime = 0;
    }

    @Override
    public void addFrame(Drawable frame, int duration) {
        super.addFrame(frame, duration);
        totalTime += duration;
    }

    @Override
    public void start() {
        super.start();
        new Handler().postDelayed(new Runnable() {

            public void run() {
                onAnimationFinish();
            }
        }, totalTime);
    }

    public int getTotalTime() {
        return totalTime;
    }

    @Override
    public void draw(Canvas canvas) {
        try {
            //Loading image from assets, you could make it from resources 
            Bitmap bmp = BitmapFactory.decodeStream(context.getAssets().open("presentation/intro_000"+(current < 10 ? "0"+current : current)+".jpg"));
            //Scaling image to fitCenter
            Matrix m = new Matrix();
            m.setRectToRect(new RectF(0, 0, bmp.getWidth(), bmp.getHeight()), new RectF(0, 0, reqWidth, reqHeight), Matrix.ScaleToFit.CENTER);
            bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), m, true);
            //Calculating the start 'x' and 'y' to paint the Bitmap 
            int x = (reqWidth - bmp.getWidth()) / 2;
            int y = (reqHeight - bmp.getHeight()) / 2;
            //Painting Bitmap in canvas
            canvas.drawBitmap(bmp, x, y, null);
            //Jump to next item
            current++;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    abstract void onAnimationFinish();

}

Теперь, чтобы воспроизвести анимацию, вам нужно сделать следующее:

    //Get your ImageView
    View image = MainActivity.this.findViewById(R.id.presentation);

    //Create AnimationDrawable
    final AnimationDrawable animation = new MyAnimationDrawable(this, displayMetrics.widthPixels, displayMetrics.heightPixels) {

        @Override
        void onAnimationFinish() {
            //Do something when finish animation
        }

    };
    animation.setOneShot(true); //dont repeat animation
    //This is just to say that my AnimationDrawable has 72 frames with 50 milliseconds interval
    try {
        //Always load same bitmap, anyway you load the right one in draw() method in MyAnimationDrawable
        Bitmap bmp = BitmapFactory.decodeStream(MainActivity.this.getAssets().open("presentation/intro_00000.jpg"));
        for (int i = 0; i < 72; i++) {
            animation.addFrame(new BitmapDrawable(getResources(), bmp), 50);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    //Set AnimationDrawable to ImageView
    if (Build.VERSION.SDK_INT < 16) {
        image.setBackgroundDrawable(animation);
    } else {
        image.setBackground(animation);
    }

    //Start animation
    image.post(new Runnable() {

        @Override
        public void run() {
            animation.start();
        }

    });

Вот и все, и работает ОК, форма меня!!!

Ответ 3

Попробуйте использовать AnimationDrawable для ImageView. Например, см. Мой ответ здесь.

Ответ 4

Самый эффективный способ показать анимацию кадров по кадрам в android использует OpenGL с NDK.

Ответ 5

Создайте изображение с помощью программирования, а затем поверните его под углом, а затем создайте еще один, а затем поверните его. сделайте это для количества изображений, которое вы хотите показать. вам нужно добавить только одно изображение. вы можете повернуть изображение следующим образом.

Matrix matrix=new Matrix();
imageView.setScaleType(ScaleType.MATRIX);   //required
matrix.postRotate((float) angle, pivX, pivY);
imageView.setImageMatrix(matrix);

Ответ 6

Может быть, глупый ответ, но это может вам помочь. Сделайте анимацию с помощью другого инструмента и сохраните ее как высококачественное видео, а затем просто воспроизведите видео.

Ответ 8

Загрузка растровых изображений из активов вместо ресурсов, сохраненных для меня на 40% времени декодирования. Вы можете попробовать это.

Итак, это продолжалось от 5 секунд с ресурсами до 3 секунд на ваших фотографиях на моем Nexus 7:

long time = System.currentTimeMillis();
try {
    for (int i = 0; i < 35; i++) {
        InputStream assetStream = getAssets().open(
                "_step" + (i + 1) + ".png");
        try {
            Bitmap bitmap = BitmapFactory.decodeStream(assetStream);
            if (bitmap == null) {
                throw new RuntimeException("Could not load bitmap");
            }
            mBitmaps.add(bitmap);
        } finally {
            assetStream.close();
        }
    }
} catch (Exception e) {
    throw new RuntimeException(e);
}
Log.d("ANIM", "Loading bitmaps elapsed "+(System.currentTimeMillis() - time)+"ms");