使用SurfaceView加载多张大分辨率图片做帧动画,解决OOM问题
来源:互联网 发布:openfire源码下载 编辑:程序博客网 时间:2024/06/09 21:04
项目需求:动态背景
继承SurfaceView的类FrameAnimation:
主界面:MainActivity
主布局:
需求很简单,只是用帧动画做一个动态的背景而已,但若是70多张图片都是1920*1080,一张485k的话,传统意义上的帧动画就很难实现了,往往加载10张就开始OOM。
一般来说,常用的实现动态背景的有效方式有三种:
①视频:果断粗暴,清晰度很有保证,但是在无限轮播重复的时候,总会有一瞬间的卡顿,这真的很让人郁闷。
②GIF动态图:直接用Glide加载就可以实现无限重复的动态背景,而且衔接没有丝毫停顿,但是有个问题,清晰度很难保证,有时候GIF太大,加载也会很缓慢。
③帧动画:清晰度非常高,但是等着OOM吧!
下面我们使用第四种方法,继承SurfaceView,实现加载多张大图片,完成真正意义上的帧动画。
图片资源接口:PictureInterface
package com.giousa.frameanimationtest;/** * Description: * Author:Giousa */public interface PictureInterface { int[] srcId = {R.drawable.a_00000, R.drawable.a_00001, R.drawable.a_00002, R.drawable.a_00003, R.drawable.a_00004, R.drawable.a_00005, R.drawable.a_00006, R.drawable.a_00007, R.drawable.a_00008, R.drawable.a_00009, R.drawable.a_00010, R.drawable.a_00011, R.drawable.a_00012, R.drawable.a_00013, R.drawable.a_00014, R.drawable.a_00015, R.drawable.a_00016, R.drawable.a_00017, R.drawable.a_00018, R.drawable.a_00019 , R.drawable.a_00020, R.drawable.a_00021, R.drawable.a_00022, R.drawable.a_00023, R.drawable.a_00024, R.drawable.a_00025, R.drawable.a_00026, R.drawable.a_00027, R.drawable.a_00028, R.drawable.a_00029 , R.drawable.a_00030, R.drawable.a_00031, R.drawable.a_00032, R.drawable.a_00033, R.drawable.a_00034, R.drawable.a_00035, R.drawable.a_00036, R.drawable.a_00037, R.drawable.a_00038, R.drawable.a_00039 , R.drawable.a_00040, R.drawable.a_00041, R.drawable.a_00042, R.drawable.a_00043, R.drawable.a_00044, R.drawable.a_00045, R.drawable.a_00046, R.drawable.a_00047, R.drawable.a_00048, R.drawable.a_00049 , R.drawable.a_00050, R.drawable.a_00051, R.drawable.a_00052, R.drawable.a_00053, R.drawable.a_00054, R.drawable.a_00055, R.drawable.a_00056, R.drawable.a_00057, R.drawable.a_00058, R.drawable.a_00059 , R.drawable.a_00060, R.drawable.a_00061, R.drawable.a_00062, R.drawable.a_00063, R.drawable.a_00064, R.drawable.a_00065, R.drawable.a_00066, R.drawable.a_00067, R.drawable.a_00068, R.drawable.a_00069 , R.drawable.a_00070, R.drawable.a_00071, R.drawable.a_00072, R.drawable.a_00073, R.drawable.a_00074, R.drawable.a_00075 };}
继承SurfaceView的类FrameAnimation:
package com.giousa.frameanimationtest;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.os.Handler;import android.util.AttributeSet;import android.util.Log;import android.view.KeyEvent;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.util.ArrayList;/** * Description: * Author:Giousa * Date:2016/11/4 * Email:65489469@qq.com */public class FrameAnimation extends SurfaceView implements SurfaceHolder.Callback, Runnable { private SurfaceHolder mSurfaceHolder; private boolean mIsThreadRunning = true; // 线程运行开关 public static boolean mIsDestroy = false;// 是否已经销毁 private int[] mBitmapResourceIds;// 用于播放动画的图片资源id数组 private ArrayList<String> mBitmapResourcePaths;// 用于播放动画的图片资源path数组 private int totalCount;//资源总数 private Canvas mCanvas; private Bitmap mBitmap;// 显示的图片 private int mCurrentIndext;// 当前动画播放的位置 private int mGapTime = 150;// 每帧动画持续存在的时间 private boolean mIsRepeat = false; private OnFrameFinishedListener mOnFrameFinishedListener;// 动画监听事件 public FrameAnimation(Context context) { this(context, null); initView(); } public FrameAnimation(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); initView(); } public FrameAnimation(Context context, AttributeSet attrs) { this(context, attrs, 0); initView(); } private void initView() { mSurfaceHolder = this.getHolder(); mSurfaceHolder.addCallback(this); // 白色背景 setZOrderOnTop(true); setZOrderMediaOverlay(true); } @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { // 当surfaceView销毁时, 停止线程的运行. 避免surfaceView销毁了线程还在运行而报错.// mIsThreadRunning = false;// try {// Thread.sleep(mGapTime);// } catch (InterruptedException e) {// e.printStackTrace();// }//// mIsDestroy = true; } /** * 制图方法 */ private void drawView() { // 无资源文件退出 if (mBitmapResourceIds == null && mBitmapResourcePaths == null) { Log.e("frameview", "the bitmapsrcIDs is null"); mIsThreadRunning = false; return; } // 锁定画布 if(mSurfaceHolder != null){ mCanvas = mSurfaceHolder.lockCanvas(); } try { if (mSurfaceHolder != null && mCanvas != null) { mCanvas.drawColor(Color.WHITE); if (mBitmapResourceIds != null && mBitmapResourceIds.length > 0) mBitmap = BitmapFactory.decodeResource(getResources(), mBitmapResourceIds[mCurrentIndext]); else if (mBitmapResourcePaths != null && mBitmapResourcePaths.size() > 0) { mBitmap = BitmapFactory.decodeFile(mBitmapResourcePaths.get(mCurrentIndext)); } Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); Rect mSrcRect, mDestRect; mSrcRect = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight()); mDestRect = new Rect(0, 0, getWidth(), getHeight()); mCanvas.drawBitmap(mBitmap, mSrcRect, mDestRect, paint); // 播放到最后一张图片 if (mCurrentIndext == totalCount - 1) { //TODO 设置重复播放 //播放到最后一张,当前index置零 mCurrentIndext = 0; } } } catch (Exception e) { e.printStackTrace(); } finally { mCurrentIndext++; if(mCurrentIndext >= totalCount){ mCurrentIndext = 0; } if (mCanvas != null) { // 将画布解锁并显示在屏幕上 if(mSurfaceHolder!=null){ mSurfaceHolder.unlockCanvasAndPost(mCanvas); } } if (mBitmap != null) { // 收回图片 mBitmap.recycle(); } } } @Override public void run() { if (mOnFrameFinishedListener != null) { mOnFrameFinishedListener.onStart(); } // 每隔150ms刷新屏幕 while (mIsThreadRunning) { drawView(); try { Thread.sleep(mGapTime); } catch (Exception e) { e.printStackTrace(); } } if (mOnFrameFinishedListener != null) { mOnFrameFinishedListener.onStop(); } } /** * 开始动画 */ public void start() { if (!mIsDestroy) { mCurrentIndext = 0; mIsThreadRunning = true; new Thread(this).start(); } else { // 如果SurfaceHolder已经销毁抛出该异常 try { throw new Exception("IllegalArgumentException:Are you sure the SurfaceHolder is not destroyed"); } catch (Exception e) { e.printStackTrace(); } } } /** * 设置动画播放素材的id * * @param bitmapResourceIds 图片资源id */ public void setBitmapResoursID(int[] bitmapResourceIds) { this.mBitmapResourceIds = bitmapResourceIds; totalCount = bitmapResourceIds.length; } /** * 设置动画播放素材的路径 * * @param bitmapResourcePaths */ public void setmBitmapResourcePath(ArrayList bitmapResourcePaths) { this.mBitmapResourcePaths = bitmapResourcePaths; totalCount = bitmapResourcePaths.size(); } /** * 设置每帧时间 */ public void setGapTime(int gapTime) { this.mGapTime = gapTime; } /** * 结束动画 */ public void stop() { mIsThreadRunning = false; } /** * 继续动画 */ public void reStart() { mIsThreadRunning = false; } /** * 设置动画监听器 */ public void setOnFrameFinisedListener(OnFrameFinishedListener onFrameFinishedListener) { this.mOnFrameFinishedListener = onFrameFinishedListener; } /** * 动画监听器 * * @author qike */ public interface OnFrameFinishedListener { /** * 动画开始 */ void onStart(); /** * 动画结束 */ void onStop(); } /** * 当用户点击返回按钮时,停止线程,反转内存溢出 */ @Override public boolean onKeyDown(int keyCode, KeyEvent event) { // 当按返回键时,将线程停止,避免surfaceView销毁了,而线程还在运行而报错 if (keyCode == KeyEvent.KEYCODE_BACK) { mIsThreadRunning = false; } return super.onKeyDown(keyCode, event); }}
主界面:MainActivity
package com.giousa.frameanimationtest;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;public class MainActivity extends AppCompatActivity implements PictureInterface{ private final String TAG = MainActivity.class.getSimpleName(); private FrameAnimation mFrameAnimation; private Button mSecond; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); initData(); } private void initView() { mFrameAnimation = (FrameAnimation) findViewById(R.id.frame_animation); mSecond = (Button) findViewById(R.id.btn_second); mSecond.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(MainActivity.this,SecondActivity.class); startActivity(intent); } }); } private void initData() { initAnimation(); } private void initAnimation() { //设置资源文件 mFrameAnimation.setBitmapResoursID(srcId); //设置监听事件 mFrameAnimation.setOnFrameFinisedListener(new FrameAnimation.OnFrameFinishedListener() { @Override public void onStop() { Log.e(TAG, "stop"); } @Override public void onStart() { Log.e(TAG, "start"); Log.e(TAG, Runtime.getRuntime().totalMemory() / 1024 + "k"); } }); //设置单张图片展示时长 mFrameAnimation.setGapTime(150); mFrameAnimation.start(); }}
主布局:
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.giousa.frameanimationtest.MainActivity"> <com.giousa.frameanimationtest.FrameAnimation android:id="@+id/frame_animation" android:layout_width="match_parent" android:layout_height="match_parent"/> <Button android:gravity="center" android:textSize="25sp" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="跳转到第二界面" android:layout_alignParentBottom="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_marginBottom="88dp" android:id="@+id/btn_second"/></RelativeLayout>
~~~~~~~~~Demo资源下载~~~~~~~~~
1 0
- 使用SurfaceView加载多张大分辨率图片做帧动画,解决OOM问题
- 解决animation-list加载多张帧动画导致OOM的问题---surfaceview用法
- Android 用surfaceview模拟帧动画的效果,解决帧动画的OOM问题
- 解决加载图片OOM
- Android异步加载图片,解决图片过大OOM问题
- 使用LRUCACHE解决加载本地大量图片卡顿及OOM问题
- Android逐帧动画,逐帧动画加载图片过多时OOM异常的解决和替代方法
- Android实现图片的加载与释放(解决OOM问题)
- 教你怎么十秒解决百张图片帧动画oom终极骚问题
- 帧动画加载大量图片OOM的解决办法
- android 图片加载,OOM 问题
- android加载图片OOM问题
- 加载大图,解决oom问题
- Android DrawableAnimation逐帧动画加载多图(OOM的解决)
- Android DrawableAnimation逐帧动画加载多图(OOM的解决)
- 详解使用LruCache来解决图片OOM的问题
- 解决加载图片出现OOM的方法
- ListView异步加载图片,解决OOM
- 对freemarker的理解
- linux内存管理模型
- dos命令导入导出数据库
- iOS应用商店版本崩溃,调试不崩溃
- Kafka中ConsumerConfig源码介绍
- 使用SurfaceView加载多张大分辨率图片做帧动画,解决OOM问题
- SM4国密算法实现分析
- cascade和inverse
- 02课后作业02
- 总结2
- 二叉树的遍历(物理算法)
- scala类型系统:7) 中缀类型
- 微信小程序事件 bindtap @import 修改js
- 波形音频(WAVE)底层接口的学习与使用