SurfaceView简单使用--可做帧动画
来源:互联网 发布:苹果教育软件 编辑:程序博客网 时间:2024/06/01 22:09
公司做视频直播的礼物动效。
前期调研的过程中发现很多竞品竟然都是利用帧动画做的。
利用帧动画当然不能直接加载多张图片,要知道最大的礼物有一百多张图片,有OOM的风险。
所以利用SurfaceView实现了帧动画。这样可以控制内存一直处于非常底的范围内抖动。所占的CPU也比较小。
另外一种实现方案就是利用webp,直接播放webp.
webp相较与SurfaceView的帧动画优势就是内存占用更小,但是CPU占比会稍微大一点。
下面就是利用SurfaceView实现的帧动画:
public class SpecialGiftSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable { private static final String TAG = "Surface"; private static final long INTERVAL_TIME = 66;//最大间隔时间,每帧时间为最大时间减去加载图片消耗的时间。 private SurfaceHolder mHolder; private boolean isDrawing = false; private boolean isSurfaceCreated = false; private List<String> mFilePathListRGB = new ArrayList<>(); private List<String> mFilePathListAlpha = new ArrayList<>(); private HandlerThread handlerThread = new HandlerThread("surfaceview"); private RectF mRectF; private OnFrameAnimationListener mListener; private Handler mWorkHandler; public SpecialGiftSurfaceView(Context context) { super(context); init(); } public SpecialGiftSurfaceView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mHolder = getHolder(); mHolder.addCallback(this); //设置SurfaceView透明 setZOrderOnTop(true); mHolder.setFormat(PixelFormat.TRANSLUCENT); handlerThread.start(); } /** * 开始帧动画 * @param pathListRGB 彩色图片路径 * @param pathListAlpha 透明图片路径 */ public void startAnimation(List<String> pathListRGB, List<String> pathListAlpha) { long delay = 0; if (pathListRGB == null || pathListRGB.size() == 0) { return; } setVisibility(VISIBLE); mFilePathListRGB.clear(); mFilePathListRGB.addAll(pathListRGB); mFilePathListAlpha.clear(); if (pathListAlpha != null && mFilePathListAlpha.size() > 0) { mFilePathListAlpha.addAll(pathListAlpha); } mWorkHandler = new Handler(handlerThread.getLooper()); if (!isSurfaceCreated) { Log.d(TAG, "SurfaceView is not created.wait 1000"); delay = 1000; } setLayerType(LAYER_TYPE_HARDWARE, null); mWorkHandler.postDelayed(this, delay); } public void startAnimation(List<String> pathListRGB) { startAnimation(pathListRGB, null); } /** * 停止动画 */ public void stopAnimation() { mFilePathListRGB.clear(); mFilePathListAlpha.clear(); setVisibility(INVISIBLE); setLayerType(LAYER_TYPE_NONE, null); isDrawing = false; if (mWorkHandler != null) { mWorkHandler.removeCallbacks(this); } } public void setListener(OnFrameAnimationListener listener) { mListener = listener; } @Override public void surfaceCreated(SurfaceHolder holder) { isSurfaceCreated = true; isDrawing = true; Log.d(TAG, "surfaceCreated"); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { stopAnimation(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == event.KEYCODE_BACK) { stopAnimation(); } return super.onKeyDown(keyCode, event); } @Override public void run() { SpecialGiftSurfaceView.this.post(new Runnable() { @Override public void run() { notifyStart(); } }); for (int i = 0; i < mFilePathListRGB.size() ; i++) { if (isDrawing) { try { long temp = System.currentTimeMillis(); if (mFilePathListAlpha != null && mFilePathListAlpha.size() > 0) { draw(mFilePathListRGB.get(i), mFilePathListAlpha.get(i)); } else { draw(mFilePathListRGB.get(i)); } //间隔幅度越小,CPU占比越大。所以应该合理设置。 long ll = System.currentTimeMillis() - temp; Log.d(TAG, "id :" + i + " temp :" + ll); Thread.sleep(Math.max(0, (INTERVAL_TIME- ll))); } catch (Exception e) { e.printStackTrace(); } } else { break; } } SpecialGiftSurfaceView.this.post(new Runnable() { @Override public void run() { stopAnimation(); notifyFinished(); } }); } private void draw(String path) { Canvas canvas = mHolder.lockCanvas(); if (canvas != null) { Bitmap diskBitmap = getDiskBitmap(path); if (diskBitmap != null) { canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); mRectF = new RectF(SpecialGiftSurfaceView.this.getLeft(), SpecialGiftSurfaceView.this.getTop(), SpecialGiftSurfaceView.this.getWidth(), SpecialGiftSurfaceView.this.getHeight()); canvas.drawBitmap(diskBitmap, null, mRectF, null); } mHolder.unlockCanvasAndPost(canvas); } } private void draw(String pathRGB, String pathAlpha) { Canvas canvas = mHolder.lockCanvas(); if (canvas != null) { int saveCount = canvas.getSaveCount(); Paint l = new Paint(); l.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); Paint m = new Paint(); m.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(new float[]{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f}))); Bitmap decodeFile = getDiskBitmap(pathRGB); Bitmap decodeFile2 = getDiskBitmap(pathAlpha); if (!(decodeFile == null || decodeFile2 == null)) { Bitmap r = Bitmap.createBitmap(decodeFile.getWidth(), decodeFile.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(r); RectF rectF = new RectF(SpecialGiftSurfaceView.this.getLeft(), SpecialGiftSurfaceView.this.getTop(), SpecialGiftSurfaceView.this.getWidth(), SpecialGiftSurfaceView.this.getHeight()); c.drawBitmap(decodeFile2, 0.0f, 0.0f, m); c.drawBitmap(decodeFile, 0.0f, 0.0f, l); canvas.drawBitmap(r, null, rectF, null); canvas.restoreToCount(saveCount); } mHolder.unlockCanvasAndPost(canvas); } } private Bitmap getDiskBitmap(String pathString) { Bitmap bitmap = null; try { File file = new File(pathString); if (file.exists()) { bitmap = BitmapFactory.decodeFile(pathString); } } catch (Exception e) { e.printStackTrace(); } return bitmap; } private void notifyStart() { if (mListener != null) { mListener.onFrameAnimationStart(); } } private void notifyFinished() { if (mListener != null) { mListener.onFrameAnimationFinished(); } } public interface OnFrameAnimationListener { void onFrameAnimationStart(); void onFrameAnimationFinished(); }
其对外的接口有两个:
startAnimation(List pathListRGB)
startAnimation(List pathListRGB, List pathListAlpha)
这里提供两个重载的方法是有不同的含义的。
前提:
动画一定要是透明的,因为在播放礼物的同时,也一定要能看到主播的直播画面。
而且我们都知道PNG是支持透明的,而JPG是不支持透明的。
一个参数的方法,需要出入一组PNG图片的地址。
两个参数的方法,需要传入两组JPG图片的地址,第一组是正常的图片,带白色底。第二组是其蒙版,利用第二组将最后绘画出来的图片变为透明。
(这个方法是拆分猎豹的APK发现的,这样做的好处就是,JPG占的内存回避PNG的少,毕竟有很多礼物,需要的图片太多,能小则小。)
阅读全文
0 0
- SurfaceView简单使用--可做帧动画
- 使用SurfaceView实现简单的红包雨动画
- SurfaceView的简单使用
- SurfaceView简单使用
- Android SurfaceView简单使用
- SurfaceView的简单使用
- 使用SurfaceView播放gif动画
- 浅析SurfaceView使用surfaceview制作你想要的动画
- Android开发简单使用surfaceview
- surfaceview画图板的简单使用
- Android-使用SurfaceView多线程绘制动画
- 使用SurfaceView模拟下雪花的动画
- 为什么要使用SurfaceView来实现动画?
- 使用SurfaceView实现飘赞动画
- 使用SurfaceView实现飘赞动画
- 简单使用SurfaceView实现小球跑动
- Android中Surfaceview的简单使用
- Android使用SurfaceView播放视频 简单介绍
- dip1——introduction
- Recyclerview显示数据+GreenDao数据库+跳转传值(图片圆)+MVP
- decltype推导规则
- 命名规范(一)
- 浅谈C++类中的公有和私有
- SurfaceView简单使用--可做帧动画
- 莫比乌斯反演
- C语言结构体的字节对齐原则
- 集合乱序方法
- 数组排序
- PIC16模拟串口以及采样正弦波
- C#泛型类访问子类成员
- css3-颜色背景盒模型
- L