Android 逐帧动态壁纸动画

Android frame by frame live wallpaper animation

我有问题。我用逐帧动画为 android 编写了动态壁纸,我已经完成了,但是动画播放速度非常慢,大约每秒 1 帧。

这是我的代码

    public class WallpaperSer extends WallpaperService {

    static final Handler mHandler = new Handler();

    public void onCreate() {
        super.onCreate();
    }

    public void onDestroy() {
        super.onDestroy();
    }

    public Engine onCreateEngine() {
        return new WallpaperSerEngine();
    }

    class WallpaperSerEngine extends Engine  {
        private boolean mVisible;
        private float mScaleX;
        private float mScaleY;
        private Bitmap icon;
        private Runnable diwaliImg;

        int i = 0;
        int[] pirates = {
                R.drawable.final_0001,
                R.drawable.final_0002,
                R.drawable.final_0003,
                R.drawable.final_0004,
                R.drawable.final_0005,
                R.drawable.final_0006,
                R.drawable.final_0007,
                R.drawable.final_0008,
                R.drawable.final_0009,
                R.drawable.final_0010,
                R.drawable.final_0011,
                R.drawable.final_0012,
                R.drawable.final_0013,
                R.drawable.final_0014,
                R.drawable.final_0015,
                R.drawable.final_0016,
                R.drawable.final_0017,
                R.drawable.final_0018,
                R.drawable.final_0019,
                R.drawable.final_0020,
                R.drawable.final_0021,
                R.drawable.final_0022,
                R.drawable.final_0023,
                R.drawable.final_0024,
                R.drawable.final_0025,
                R.drawable.final_0026,
                R.drawable.final_0027,
                R.drawable.final_0028,
                R.drawable.final_0029,
                R.drawable.final_0030,
                R.drawable.final_0031,
                R.drawable.final_0032,
                R.drawable.final_0033,
                R.drawable.final_0034,
                R.drawable.final_0035,
                R.drawable.final_0036,
                R.drawable.final_0037,
                R.drawable.final_0038,
                R.drawable.final_0039,
                R.drawable.final_0040,
                R.drawable.final_0041,
                R.drawable.final_0042,
                R.drawable.final_0043,
                R.drawable.final_0044,
                R.drawable.final_0045,
                R.drawable.final_0046,
                R.drawable.final_0047,
                R.drawable.final_0048,
                R.drawable.final_0049,
                R.drawable.final_0050,
                R.drawable.final_0051,
                R.drawable.final_0052,
                R.drawable.final_0053,
                R.drawable.final_0054,
                R.drawable.final_0065,
                R.drawable.final_0056,
                R.drawable.final_0057,
                R.drawable.final_0058,
                R.drawable.final_0059,
                R.drawable.final_0060,
                R.drawable.final_0061,
                R.drawable.final_0062,
                R.drawable.final_0063,
                R.drawable.final_0064,
                R.drawable.final_0065,
                R.drawable.final_0066,
                R.drawable.final_0067,
                R.drawable.final_0068,
                R.drawable.final_0069,
                R.drawable.final_0070,
                R.drawable.final_0071,
                R.drawable.final_0072,
                R.drawable.final_0073,
                R.drawable.final_0074,
                R.drawable.final_0075,
                R.drawable.final_0076,
                R.drawable.final_0077,
                R.drawable.final_0078,
                R.drawable.final_0079,
                R.drawable.final_0080,
                R.drawable.final_0081,
                R.drawable.final_0082,
                R.drawable.final_0083,
                R.drawable.final_0084,
                R.drawable.final_0085,
                R.drawable.final_0086,
                R.drawable.final_0087,
                R.drawable.final_0088,
                R.drawable.final_0089,
                R.drawable.final_0090,
                R.drawable.final_0091,
                R.drawable.final_0092,
                R.drawable.final_0093,
                R.drawable.final_0094,
                R.drawable.final_0095,
                R.drawable.final_0096,
                R.drawable.final_0097,
                R.drawable.final_0098,
                R.drawable.final_0099,
                R.drawable.final_0100,
                R.drawable.final_0101,
                R.drawable.final_0102,
                R.drawable.final_0103,
                R.drawable.final_0104,
                R.drawable.final_0105,
                R.drawable.final_0106,
                R.drawable.final_0107,
                R.drawable.final_0108,
                R.drawable.final_0109,
                R.drawable.final_0110,
                R.drawable.final_0111,
                R.drawable.final_0112,
                R.drawable.final_0113,
                R.drawable.final_0114,
                R.drawable.final_0115,
                R.drawable.final_0116,
                R.drawable.final_0117,
                R.drawable.final_0118,
                R.drawable.final_0119,
                R.drawable.final_0120
        };

        WallpaperSerEngine() {
            diwaliImg = new Runnable() {
                public void run() {
                    drawFrame();
                }
            };
        }

        @Override
        public void onCreate(SurfaceHolder holder){
            super.onCreate(holder);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacks(diwaliImg);
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            mVisible = visible;
            if (visible) {
                drawFrame();
            } else {
                mHandler.removeCallbacks(diwaliImg);
            }
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            super.onSurfaceChanged(holder, format, width, height);
            drawFrame();
            mScaleX = width / (1f * icon.getWidth());
            mScaleY = height / (1f * icon.getHeight());
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder) {
            super.onSurfaceDestroyed(holder);
            mVisible = false;
            mHandler.removeCallbacks(diwaliImg);
        }


        @Override
        public void onOffsetsChanged(float xOffset, float yOffset, float xStep,float yStep, int xPixels, int yPixels) {
            drawFrame();
        }

        @Override
        public void onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
        }

        private void drawFrame() {
            final SurfaceHolder holder = getSurfaceHolder();
            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    drawPirate(c);
                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
            mHandler.removeCallbacks(diwaliImg);
            if (mVisible) {
                mHandler.postDelayed(diwaliImg, 1000 / 25);
            }
        }

        private void drawPirate(Canvas c) {
            icon = BitmapFactory.decodeResource(getResources(), pirates[i]);
            i++;
            if (i == 120) {
                i = 0;
            }
            Matrix matrix = new Matrix();
            c.scale(mScaleX, mScaleY);
            c.drawBitmap(icon, matrix, null);
            icon.recycle();
        }
    }
}

我做错了什么?

更新#1 我的错误日志与@chipopo 代码:

java.lang.IllegalArgumentException: Problem decoding into existing bitmap at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:482) at dajver.com.framebyframelivewallpaper.WallpaperSer$WallpaperSerEngine.setToFrame(WallpaperSer.java:188) at dajver.com.framebyframelivewallpaper.WallpaperSer$WallpaperSerEngine.drawPirate(WallpaperSer.java:266) at dajver.com.framebyframelivewallpaper.WallpaperSer$WallpaperSerEngine.drawFrame(WallpaperSer.java:253) at dajver.com.framebyframelivewallpaper.WallpaperSer$WallpaperSerEngine.onSurfaceChanged(WallpaperSer.java:219) at android.service.wallpaper.WallpaperService$Engine.updateSurface(WallpaperService.java:783) at android.service.wallpaper.WallpaperService$Engine.attach(WallpaperService.java:890) at android.service.wallpaper.WallpaperService$IWallpaperEngineWrapper.executeMessage(WallpaperService.java:1157) at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5221) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)

我的代码包含@chipppo 编写的修复程序

public class WallpaperSer extends WallpaperService {

    static final Handler mHandler = new Handler();

    public void onCreate() {
        super.onCreate();
    }

    public void onDestroy() {
        super.onDestroy();
    }

    public Engine onCreateEngine() {
        return new WallpaperSerEngine();
    }

    class WallpaperSerEngine extends Engine  {
        private boolean mVisible;
        private float mScaleX;
        private float mScaleY;
        private Bitmap icon;
        private Runnable diwaliImg;

        int i = 0;
        int[] pirates = {
                R.drawable.final_0001,
                R.drawable.final_0002,
                R.drawable.final_0003,
                R.drawable.final_0004,
                R.drawable.final_0005,
                R.drawable.final_0006,
                R.drawable.final_0007,
                R.drawable.final_0008,
                R.drawable.final_0009,
                R.drawable.final_0010,
                R.drawable.final_0011,
                R.drawable.final_0012,
                R.drawable.final_0013,
                R.drawable.final_0014,
                R.drawable.final_0015,
                R.drawable.final_0016,
                R.drawable.final_0017,
                R.drawable.final_0018,
                R.drawable.final_0019,
                R.drawable.final_0020,
                R.drawable.final_0021,
                R.drawable.final_0022,
                R.drawable.final_0023,
                R.drawable.final_0024,
                R.drawable.final_0025,
                R.drawable.final_0026,
                R.drawable.final_0027,
                R.drawable.final_0028,
                R.drawable.final_0029,
                R.drawable.final_0030,
                R.drawable.final_0031,
                R.drawable.final_0032,
                R.drawable.final_0033,
                R.drawable.final_0034,
                R.drawable.final_0035,
                R.drawable.final_0036,
                R.drawable.final_0037,
                R.drawable.final_0038,
                R.drawable.final_0039,
                R.drawable.final_0040,
                R.drawable.final_0041,
                R.drawable.final_0042,
                R.drawable.final_0043,
                R.drawable.final_0044,
                R.drawable.final_0045,
                R.drawable.final_0046,
                R.drawable.final_0047,
                R.drawable.final_0048,
                R.drawable.final_0049,
                R.drawable.final_0050,
                R.drawable.final_0051,
                R.drawable.final_0052,
                R.drawable.final_0053,
                R.drawable.final_0054,
                R.drawable.final_0065,
                R.drawable.final_0056,
                R.drawable.final_0057,
                R.drawable.final_0058,
                R.drawable.final_0059,
                R.drawable.final_0060,
                R.drawable.final_0061,
                R.drawable.final_0062,
                R.drawable.final_0063,
                R.drawable.final_0064,
                R.drawable.final_0065,
                R.drawable.final_0066,
                R.drawable.final_0067,
                R.drawable.final_0068,
                R.drawable.final_0069,
                R.drawable.final_0070,
                R.drawable.final_0071,
                R.drawable.final_0072,
                R.drawable.final_0073,
                R.drawable.final_0074,
                R.drawable.final_0075,
                R.drawable.final_0076,
                R.drawable.final_0077,
                R.drawable.final_0078,
                R.drawable.final_0079,
                R.drawable.final_0080,
                R.drawable.final_0081,
                R.drawable.final_0082,
                R.drawable.final_0083,
                R.drawable.final_0084,
                R.drawable.final_0085,
                R.drawable.final_0086,
                R.drawable.final_0087,
                R.drawable.final_0088,
                R.drawable.final_0089,
                R.drawable.final_0090,
                R.drawable.final_0091,
                R.drawable.final_0092,
                R.drawable.final_0093,
                R.drawable.final_0094,
                R.drawable.final_0095,
                R.drawable.final_0096,
                R.drawable.final_0097,
                R.drawable.final_0098,
                R.drawable.final_0099,
                R.drawable.final_0100,
                R.drawable.final_0101,
                R.drawable.final_0102,
                R.drawable.final_0103,
                R.drawable.final_0104,
                R.drawable.final_0105,
                R.drawable.final_0106,
                R.drawable.final_0107,
                R.drawable.final_0108,
                R.drawable.final_0109,
                R.drawable.final_0110,
                R.drawable.final_0111,
                R.drawable.final_0112,
                R.drawable.final_0113,
                R.drawable.final_0114,
                R.drawable.final_0115,
                R.drawable.final_0116,
                R.drawable.final_0117,
                R.drawable.final_0118,
                R.drawable.final_0119,
                R.drawable.final_0120
        };

        private Bitmap mainBitmap;
        private BitmapFactory.Options mBitmapOptions;
        private int currentFrameIndex = 0;

        WallpaperSerEngine() {
            diwaliImg = new Runnable() {
                public void run() {
                    drawFrame();
                }
            };
            initMainBitmap(getResources(), pirates[0]);
        }

        private void initMainBitmap(Resources res,int resId){
            mBitmapOptions = new BitmapFactory.Options();
            mBitmapOptions.inJustDecodeBounds = true;
            mBitmapOptions.inMutable = true;
            BitmapFactory.decodeResource(res, resId,mBitmapOptions);

            mainBitmap = Bitmap.createBitmap(mBitmapOptions.outWidth,mBitmapOptions.outHeight,Bitmap.Config.ARGB_8888);
            mBitmapOptions.inJustDecodeBounds = false;
            mBitmapOptions.inBitmap = mainBitmap;//will cause the bitmap to be reused.
            mBitmapOptions.inSampleSize = 1;
        }

        public boolean setToFrame(int frameIndex){
            if(pirates != null && frameIndex >= 0 && frameIndex< pirates.length){
                currentFrameIndex = frameIndex;
                mainBitmap = BitmapFactory.decodeResource(getResources(), pirates[currentFrameIndex], mBitmapOptions);
                return true;
            }
            else return false;
        }


        @Override
        public void onCreate(SurfaceHolder holder){
            super.onCreate(holder);
        }

        @Override
        public void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacks(diwaliImg);
        }

        @Override
        public void onVisibilityChanged(boolean visible) {
            mVisible = visible;
            if (visible) {
                drawFrame();
            } else {
                mHandler.removeCallbacks(diwaliImg);
            }
        }

        @Override
        public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            super.onSurfaceChanged(holder, format, width, height);
            drawFrame();
            mScaleX = width / (1f * icon.getWidth());
            mScaleY = height / (1f * icon.getHeight());
        }

        @Override
        public void onSurfaceCreated(SurfaceHolder holder) {
            super.onSurfaceCreated(holder);
        }

        @Override
        public void onSurfaceDestroyed(SurfaceHolder holder) {
            super.onSurfaceDestroyed(holder);
            mVisible = false;
            mHandler.removeCallbacks(diwaliImg);
        }


        @Override
        public void onOffsetsChanged(float xOffset, float yOffset, float xStep,float yStep, int xPixels, int yPixels) {
            drawFrame();
        }

        @Override
        public void onTouchEvent(MotionEvent event) {
            super.onTouchEvent(event);
        }

        private void drawFrame() {
            final SurfaceHolder holder = getSurfaceHolder();
            Canvas c = null;
            try {
                c = holder.lockCanvas();
                if (c != null) {
                    drawPirate(c);
                }
            } finally {
                if (c != null)
                    holder.unlockCanvasAndPost(c);
            }
            mHandler.removeCallbacks(diwaliImg);
            if (mVisible) {
                mHandler.postDelayed(diwaliImg, 1000 / 24);
            }
        }

        private void drawPirate(Canvas c) {
            setToFrame(i);
            i++;
            if (i == 120) {
                i = 0;
            }
            Matrix matrix = new Matrix();
            c.scale(mScaleX, mScaleY);
            c.drawBitmap(mainBitmap, matrix, null);
        }
    }
}

调用BitmapFactory.decodeResource(getResources(), pirates[i]);创建一个新的位图。 我假设您的图像都具有相同的大小,因此您可以回收单个位图,而不是在每次图像更改时创建一个新位图。这应该会显着提高您的 FPS。

回收位图:

private Bitmap mainBitmap;
private BitmapFactory.Options mBitmapOptions;
private void initMainBitmap(Resources res,int resId){
    mBitmapOptions = new BitmapFactory.Options();
    mBitmapOptions.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId,mBitmapOptions);

    mainBitmap = Bitmap.createBitmap(mBitmapOptions.outWidth,mBitmapOptions.outHeight,Bitmap.Config.ARGB_8888);
    mBitmapOptions.inJustDecodeBounds = false;
    mBitmapOptions.inBitmap = mainBitmap;//will cause the bitmap to be reused.
    mBitmapOptions.inSampleSize = 1;
}

private int currentFrameIndex = 0;
public boolean setToFrame(int frameIndex){
    if(pirates != null && frameIndex >= 0 && frameIndex< pirates.length){
        currentFrameIndex = frameIndex;
        mainBitmap = BitmapFactory.decodeResource(mResources, pirates[currentFrameIndex],mBitmapOptions);           
        return true;
    }
    else return false;
}

使用此代码:

从您的引擎构造函数调用 initMainBitmap 函数:

WallpaperSerEngine() {
    diwaliImg = new Runnable() {
        public void run() {
            drawFrame();
        }
    };        
    initMainBitmap(getResources(),pirates[0]);
}

您的 drawPirate 应更改为:

private void drawPirate(Canvas c) {
        setToFrame(i);
        i++;
        if (i == 120) {
            i = 0;
        }
        Matrix matrix = new Matrix();
        c.scale(mScaleX, mScaleY);
        c.drawBitmap(mainBitmap, matrix, null);
}