自定义控件之帧动画
来源:互联网 发布:音频转文字软件 编辑:程序博客网 时间:2024/05/16 12:44
看到了这篇文章的标题,你也大概知道了这篇文章主要讲什么:利用原生的<animation-list>标签配置帧动画时,如果图片过大,或者图片过多,就会OOM。所以,为了避免这样情况,有以下两种方式:
1. 让UI把帧动画设计成gif动画,然后使用某个gif开源框架加载,如:https://github.com/koral--/android-gif-drawable
2. 自定义控件实现帧动画
本文主要讲的是第二种方式,自定义控件实现帧动画,废话少说,直接上代码。
等等,还是先讲一下思路吧。
1. 怎么才能不OOM呢?
答:只要一次加载一张图片不就可以了,在加载第二章图片的时候,把第一张图片recycle掉。
2. 难道要直接继承view吗?
答:直接继承View也可以,但本人没那么强大的能力。本人选择的是SurfaceView。
3. 为啥选择SurfaceView?
答:
a:SurfaceView是View的继承类,这个视图里内嵌了一个专门用于绘制的Surface。你可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。
b:Surfaceview提供了两个线程:UI线程和渲染线程,他的特性是:可以在主线程之外的线程中向屏幕绘图上,这样可以避免画图任务繁重的时候造成主线程阻塞。
4. 如何像<animation-list>那样,配置图片位置,图片显示时间呢?
答:在assets中搞一个配置文件,配置好,然后在代码中解析不就行啦。
5. 额,没有5了
好啦,现在可以上代码了。
先上这个:配置文件frame_anim.txt(不要问我为什么用txt,我自己也不知道)
<anim> <frame> <icon>frame_anim/sing01.jpg</icon> <duration>300</duration> </frame> <frame> <icon>frame_anim/sing02.jpg</icon> <duration>300</duration> </frame> <frame> <icon>frame_anim/sing03.jpg</icon> <duration>300</duration> </frame> <frame> <icon>frame_anim/sing04.jpg</icon> <duration>300</duration> </frame></anim>
里面的icon路径也是assets下的。
配置文件搞定,然后上下一位:frame_anim.txt里面每一项对应的javaBean文件
public class AnimItem { /** * 图片地址 */ private String iconUrl; /** * 显示时间 */ private long duration; public String getIconUrl() { return iconUrl; } public void setIconUrl(String iconUrl) { this.iconUrl = iconUrl; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; }}
import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.util.Xml;import org.xmlpull.v1.XmlPullParser;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.ArrayList;import java.util.List;/** * className: AnimationHelper * function: 自定义帧动画辅助类 * <p> * create at 2017/5/26 15:49 * * @author pg */public class AnimationHelper { /** * 加载帧动画图片 * * @param context * @param url * @param reqWidth * @param reqHeight * @return */ public static Bitmap loadFromAsset(Context context, String url, int reqWidth, int reqHeight) { InputStream is = null; Bitmap bitmap; try { is = context.getAssets().open(url); } catch (IOException e) { e.printStackTrace(); } byte[] data = input2byte(is); BitmapFactory.Options newOpts = new BitmapFactory.Options(); // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容 newOpts.inJustDecodeBounds = true; BitmapFactory.decodeByteArray(data, 0, data.length, newOpts); newOpts.inJustDecodeBounds = false; int width = newOpts.outWidth; int height = newOpts.outHeight; // 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int inSampleSize = 1;//be=1表示不缩放 if (height > reqHeight || width > reqWidth) { // 计算出实际宽高和目标宽高的比率 final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高 // 一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } newOpts.inSampleSize = inSampleSize;//设置缩放比例 // 开始压缩图片,注意此时已经把options.inJustDecodeBounds 设回false了 bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, newOpts); //根据需求宽高,定向设置大小 bitmap = Bitmap.createScaledBitmap(bitmap, reqWidth, reqHeight, true); return bitmap; } /** * 输入流转byte[] * * @param inStream InputStream * @return Byte数组 */ public static final byte[] input2byte(InputStream inStream) { if (inStream == null) return null; ByteArrayOutputStream swapStream = new ByteArrayOutputStream(); byte[] buff = new byte[100]; int rc = 0; try { while ((rc = inStream.read(buff, 0, 100)) > 0) { swapStream.write(buff, 0, rc); } } catch (IOException e) { e.printStackTrace(); } return swapStream.toByteArray(); } /** * 解析xml文件,获取帧动画内容 * * @param context * @param file * @return * @throws Exception */ public static List<AnimItem> getAnimFromXml(Context context, String file) { //解析的结果 List<AnimItem> resultList = null; try { //获取文件流 InputStream ins = context.getAssets().open(file); //xml解析器 XmlPullParser parser = Xml.newPullParser(); parser.setInput(ins, "utf-8"); //获取节点状态 int eventType = parser.getEventType(); AnimItem subItem = null; while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { //解析到标签的开始 case XmlPullParser.START_TAG: //解析到anim,创建结果集 if ("anim".equals(parser.getName())) { resultList = new ArrayList<>(); } //检测到frame,取文件序号 else if ("frame".equals(parser.getName())) { subItem = new AnimItem(); } //检测到icon,设置icon else if ("icon".equals(parser.getName())) { parser.next(); subItem.setIconUrl(parser.getText()); } //检测到duration,设置duration else if ("duration".equals(parser.getName())) { parser.next(); subItem.setDuration(Long.parseLong(parser.getText())); } break; //检测到结束标签,把frame放入集合 case XmlPullParser.END_TAG: if ("frame".equals(parser.getName())) { resultList.add(subItem); subItem = null; } break; } eventType = parser.next(); } } catch (Exception e) { e.printStackTrace(); } return resultList; }}
在接下来,上压轴的这位:SurfaceAnimView.java
import android.content.Context;import android.graphics.Bitmap;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.PixelFormat;import android.os.Handler;import android.os.HandlerThread;import android.util.AttributeSet;import android.view.SurfaceHolder;import android.view.SurfaceView;import java.util.List;/** * className: SurfaceAnimView * function: 自定义播放帧动画组件 * <p> * create at 2017/5/26 16:11 * * @author pg */public class SurfaceAnimView extends SurfaceView { /** * 新建子线程,执行耗时任务 */ private static HandlerThread drawThread = new HandlerThread("surface_anim"); static { drawThread.start(); } private static Handler dHandler = new Handler(drawThread.getLooper()); /** * 源数据 */ private List<AnimItem> imageList; /** * 耗时操作的runnable */ private DrawRunnable mDrawRunnable; public SurfaceAnimView(Context context) { this(context, null); } public SurfaceAnimView(Context context, AttributeSet attrs) { super(context, attrs); getHolder().addCallback(callback); //设置位于顶层,解决有背景是无法显示canvas的问题 this.setZOrderOnTop(true); //设置背景透明,默认是不透明(黑色) getHolder().setFormat(PixelFormat.TRANSPARENT); } /** * 设置数据源 * * @param imageList */ public void setImageList(final List<AnimItem> imageList) { this.imageList = imageList; } SurfaceHolder.Callback callback = new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { startDraw(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { stopDraw(); } }; /** * 开始绘制帧动画 */ private void startDraw() { if (mDrawRunnable != null) { mDrawRunnable.setStop(true); dHandler.removeCallbacks(mDrawRunnable); } mDrawRunnable = new DrawRunnable(); dHandler.post(mDrawRunnable); } /** * 结束绘制帧动画 */ private void stopDraw() { if (mDrawRunnable != null) { mDrawRunnable.setStop(true); dHandler.removeCallbacks(mDrawRunnable); } } class DrawRunnable implements Runnable { private boolean isStop = false; public void setStop(boolean isStop) { this.isStop = isStop; } @Override public void run() { while (!isStop) { if (imageList != null && imageList.size() > 0) { for (int i = 0; i < imageList.size(); i++) { AnimItem item = imageList.get(i); //得到需要的bitmap Bitmap subBitmap = AnimationHelper.loadFromAsset(getContext(), item.getIconUrl(), getWidth(), getHeight()); if (subBitmap != null) { if (!isStop && getHolder() != null) { Canvas c = getHolder().lockCanvas(null); if (c != null) { //设置除去bitmap之外的区域 c.drawColor(Color.YELLOW); //绘制图片 c.drawBitmap(subBitmap, 0, 0, null); getHolder().unlockCanvasAndPost(c); } } } long duration = item.getDuration(); try { Thread.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } @Override public void setVisibility(int visibility) { super.setVisibility(visibility); }}
嗯,结束。。。。。
还是先不结束把,还没说怎么调用呢,
xml.layout中,配置宽、高、id即可
activity中,获取到这个view,然后:!!!!
List<AnimItem> animItemList = AnimationHelper.getAnimFromXml(this, "frame_anim.txt");ivFrameAnim.setImageList(animItemList);
Game Over。上完走人
- 自定义控件之帧动画
- 自定义控件之飘落动画
- Android自定义控件之扫描动画UI
- 自定义简单的逐帧动画控件
- 【自定义简单的逐帧动画控件】
- 自定义动画控件 AnimationView
- 自定义控件动画TextView
- Android自定义控件:动画类---逐帧动画AnimationDrawable
- Swift-自定义控件之IndicatorButton(带动画的按钮)
- 安卓自定义控件之带有折叠动画的ExpandableListView
- Android学习之动画效果的实现、自定义控件皮肤
- 安卓开发之自定义粒子旋转动画加载控件
- 安卓开发之使用PathMeasure自定义加载动画控件
- android自定义控件之圆形进度条(带动画)
- 自定义控件 播放GIF动画
- 自定义控件 AnimationView 动画View
- 自定义控件——动画
- 自定义控件三部曲之动画篇(九)——联合动画的代码实现
- 设计模式—模板模式(七)
- sql语句优化
- 链式A+B(解题报告)
- Android Notification 详解——基本操作(上)
- raspbian安装Apache修改配置文件实现多站点
- 自定义控件之帧动画
- 日期只选择年月
- 安装Zend Studio 13.5报错:0x80070666
- wordpress Warning: mysqli_real_connect(): (HY000/2002) php7
- Java NIO框架Netty教程(一) – Hello Netty
- JavaScript对象-(2)
- mysql into outline 导出数据到文件 --where
- android 集成极光推送
- 步进电机常见问题及解决办法