前提条件我就不说了,相信能查找这个主题的都不是一般的Android开发者,我只讲核心的部分和该文章所能解决的问题。
之前搜索过很多文章,大都不理想,无法完美的解决自己的需求,然后自己根据资料整理了一下,可以说非常完美。
解决的问题:
1、帧特别多,如果同时decode做动画可能会出现OOM的情况
2、CPU、内存占用过高
3、可以多次做重复做动画
4、代码精简易懂效率高
废话不说,代码如下:
public class BoSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "draw";
private AssetManager assetManager;
private List<String> mPathList;
private DrawThread mDrawThread;
/**
* inBitmap如果图片不是太大的话不建议打开
*/
private boolean mSupportInBitmap = true;
private SparseArray<Bitmap> mBitmapCache;
private long mFrameInterval = 48L;
private int mTotalCount;
/**
* 传入inBitmap时的decode参数
*/
private BitmapFactory.Options mOptions;
// 由于属性动画最大会当大到1.4倍,为了View能正常显示,因此绘图时必须相应都缩小1.4倍
private static final float ANIM_ZOOM = 1.4f;
private SurfaceHolder mSurfaceHolder;
private Canvas mCanvas;
private Matrix mDrawMatrix;
private State mState;
private PaintFlagsDrawFilter mDrawPaint;
private Paint mPaint = new Paint();
private int mWidth;
private int mHeight;
public BoSurfaceView(Context context) {
this(context, null);
}
public BoSurfaceView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public BoSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
assetManager = context.getAssets();
mWidth = context.getResources().getDimensionPixelSize(R.dimen.icon_width);
mHeight = context.getResources().getDimensionPixelSize(R.dimen.icon_height);
init();
}
private void init() {
mSurfaceHolder = getHolder();
mSurfaceHolder.setFormat(PixelFormat.TRANSLUCENT);
mSurfaceHolder.addCallback(this);
setZOrderOnTop(true);
mBitmapCache = new SparseArray<>();
mOptions = new BitmapFactory.Options();
mDrawPaint = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
mPaint.setFilterBitmap(true);
configureDrawMatrix();
}
public void start(State state) {
mState = state;
stop();
start(mState.toString());
}
private void start(String assetsPath) {
initPathList(FileUtil.getPathList(assetManager, assetsPath));
startDraw();
}
@Override
public void setVisibility(int visibility) {
super.setVisibility(visibility);
if (GONE == visibility || INVISIBLE == visibility) {
stop();
clearSurface();
}
}
private void initPathList(List<String> pathList) {
this.mPathList = pathList;
if (mPathList == null || mPathList.size() == 0) {
throw new NullPointerException("pathList is null.");
}
mTotalCount = mPathList.size();
Collections.sort(pathList);
}
private void startDraw() {
decodeBitmap();
mDrawThread = new DrawThread(mSurfaceHolder);
mDrawThread.startDraw();
}
private void decodeBitmap() {
if (mSupportInBitmap) {
mOptions.inMutable = true;
mOptions.inSampleSize = 1;
}
mBitmapCache.clear();
if (!mSupportInBitmap) {
for (int i = 0; i < mTotalCount; i++) {
mBitmapCache.put(i, decodeBitmapReal(mPathList.get(i)));
}
}
}
private void clearSurface() {
try {
mCanvas = mSurfaceHolder.lockCanvas();
if (mCanvas != null) {
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
public void release() {
if (mDrawThread != null) {
mDrawThread.stopDraw();
mDrawThread = null;
}
if (mOptions.inBitmap != null) {
mOptions.inBitmap.recycle();
mOptions.inBitmap = null;
}
mBitmapCache.clear();
mSurfaceHolder.removeCallback(this);
setVisibility(GONE);
}
public void stop() {
if (mDrawThread != null) {
mDrawThread.stopDraw();
}
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
stop();
}
private void configureDrawMatrix() {
mDrawMatrix = new Matrix();
float scale = 1.0f / ANIM_ZOOM;
float dx = Math.round((mWidth - mWidth * scale) * 0.5f);
float dy = Math.round((mHeight - mHeight * scale) * 0.5f);
RectF srcRect = new RectF(0, 0, 160, 120);
RectF dstRect = new RectF(0, 0, mWidth, mHeight);
mDrawMatrix.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
mDrawMatrix.postScale(scale, scale);
mDrawMatrix.postTranslate(dx, dy);
}
private Bitmap decodeBitmapReal(String path) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream(assetManager.open(path), null, mOptions);
if (mSupportInBitmap) {
mOptions.inBitmap = bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
private final class DrawThread extends Thread {
private int position;
private boolean isDrawing;
private SurfaceHolder surfaceHolder;
public DrawThread(SurfaceHolder surfaceHolder) {
this.surfaceHolder = surfaceHolder;
}
@Override
public void run() {
while (isDrawing) {
try {
drawBitmap();
Thread.sleep(mFrameInterval);
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void drawBitmap() {
Console.log.d(TAG, "drawBitmap...");
if (position >= mTotalCount) {
if (mState == State.LISTENING
|| mState == State.THINKING) {
position = 0;
} else {
isDrawing = false;
return;
}
}
Bitmap currentBitmap;
if (mSupportInBitmap) {
currentBitmap = decodeBitmapReal(mPathList.get(position));
} else {
currentBitmap = mBitmapCache.get(position);
}
if (currentBitmap == null) {
isDrawing = false;
return;
}
synchronized (surfaceHolder) {
try {
mCanvas = surfaceHolder.lockCanvas();
if (mCanvas != null) {
mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
Console.log.d(TAG, "draw position:" + position);
mCanvas.setDrawFilter(mDrawPaint);
mCanvas.drawBitmap(currentBitmap, mDrawMatrix, mPaint);
position++;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
surfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
public void startDraw() {
position = 0;
isDrawing = true;
start();
}
public void stopDraw() {
isDrawing = false;
}
}
}
调用入口是start方法,传入的是一个路径,这里我封装的是一个枚举类型。
public enum State {
/**
* 动画状态说明
*/
START("start"),
RESUME("resume"),
PAUSE("pause"),
SUCCESS("success"),
FAILURE("failure"),
STOP("stop");
private String text;
State(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
}
记住这里start可以自己根据需求去封装路径,我是从asset获取的,你可以自己封装。
需要了解更多的原理性问题或者做一些对比,可以参看一下博客和Github。
https://blog.csdn.net/binbinqq86/article/details/78127284
https://github.com/MasayukiSuda/FPSAnimator
https://github.com/yuyashuai/SilkyAnimation
这三篇文章写的都不错,但是都不适合我需要的场景,但是单独做个动画非常棒,我需要随时可以切换不同的动画状态,
使用过程非常麻烦,所以自己基于原理做了自己的封装,内存和cpu都经过严格测试非常稳定。
Thanks!