前言

首先说下为啥要通过自定义处理的方式去实现Android的帧动画效果,因为通过系统原生支持的xml和java代码这两种方式实现,在播放的图片量很多时,会出现内存溢出,此现象也是在做项目当中有遇到,出现的情景:loading视图,由于项目中的加载视图采用的是播放一组连续图片来实现动画效果。殊不知这样做是有隐患的,那就是造成了大名鼎鼎的OOM。经过几番折腾和各种尝试,最终还是决定放弃原来帧动画实现方式,另辟蹊径。

方式一:

1.定义类XAnimationDrawable,在内部采用定时器给ImageView设置图片。

2.使用步骤:

1)实例XAnimationDrawable和ImageView

XAnimationDrawable frameAnimation = new XAnimationDrawable();

ImageView iv = (ImageView)findViewById(R.id.iv_animation);

2)准备图片id资源,以下提供了两种方式

//通过代码添加图片id资源

List ids = new ArrayList();

ids.add(R.drawable.footer_loading_710000);

ids.add(R.drawable.footer_loading_710001);

......

ids.add(R.drawable.footer_loading_710015);

ids.add(R.drawable.footer_loading_710016);

//通过xml的定义,footer_loading_list.xml

android:oneshot="false">

android:drawable="@drawable/footer_loading_710000"

android:duration="60" />

android:drawable="@drawable/footer_loading_710001"

android:duration="60" />

......

android:drawable="@drawable/footer_loading_710008"

android:duration="60" />

android:drawable="@drawable/footer_loading_710009"

android:duration="60" />

3)设置播放的图片资源

//通过代码添加图片id资源对应的播放动画方式

frameAnimation.setAnimation(iv, ids);

//通过xml定义图片id资源列表对应的播放动画方式

frameAnimation.setAnimation(context, R.drawable.footer_loading_list, iv);

4)开始动画

frameAnimation.start(true, 80);

XAnimationDrawable.java

public class XAnimationDrawable {

private static final int MSG_START = 0xf1;

private static final int MSG_STOP = 0xf2;

private static final int STATE_STOP = 0xf3;

private static final int STATE_RUNNING = 0xf4;

//运行状态

private int mState = STATE_RUNNING;

//显示图片的View

private ImageView mImageView = null;

//图片资源的ID列表

private List mResourceIdList = null;

//定时任务器

private Timer mTimer = null;

//定时任务

private AnimTimerTask mTimeTask = null;

//记录播放位置

private int mFrameIndex = 0;

//播放形式

private boolean isLooping = false;

public XAnimationDrawable() {

mTimer = new Timer();

}

/**

* 设置动画播放资源

*/

public void setAnimation(ImageView imageview, List resourceIdList){

mImageView = imageview;

mResourceIdList = new ArrayList();

mResourceIdList.clear();

mResourceIdList.addAll(resourceIdList);

}

/**

* 设置动画播放资源

*/

public void setAnimation(Context context, int resourceId, ImageView imageview){

this.mImageView = imageview;

mResourceIdList = new ArrayList();

mResourceIdList.clear();

loadFromXml(context, resourceId, new OnParseListener() {

@Override

public void onParse(List res) {

mResourceIdList.addAll(res);

}

});

}

/**

* 解析xml

*

* @param context

* @param resourceId 资源id

*/

private void loadFromXml(final Context context, final int resourceId,

final OnParseListener onParseListener) {

if (context == null) {

return;

}

final List res = new ArrayList();

XmlResourceParser parser = context.getResources().getXml(resourceId);

try {

int eventType = parser.getEventType();

while (eventType != XmlPullParser.END_DOCUMENT) {

if (eventType == XmlPullParser.START_DOCUMENT) {

} else if (eventType == XmlPullParser.START_TAG) {

if (parser.getName().equals("item")) {

for (int i = 0; i < parser.getAttributeCount(); i++) {

if (parser.getAttributeName(i).equals("drawable")) {

int resId = Integer.parseInt(parser.getAttributeValue(i).substring(1));

res.add(resId);

}

}

}

} else if (eventType == XmlPullParser.END_TAG) {

} else if (eventType == XmlPullParser.TEXT) {

}

eventType = parser.next();

}

} catch (IOException e) {

// TODO: handle exception

e.printStackTrace();

} catch (XmlPullParserException e2) {

// TODO: handle exception

e2.printStackTrace();

} finally {

parser.close();

}

if (onParseListener != null) {

onParseListener.onParse(res);

}

}

/**

* 开始播放动画

* @param loop 是否循环播放

* @param duration 动画播放时间间隔

*/

public void start(boolean loop, int duration){

stop();

if (mResourceIdList == null || mResourceIdList.size() == 0) {

return;

}

if (mTimer == null) {

mTimer = new Timer();

}

isLooping = loop;

mFrameIndex = 0;

mState = STATE_RUNNING;

mTimeTask = new AnimTimerTask( );

mTimer.schedule(mTimeTask, 0, duration);

}

/**

* 停止动画播放

*/

public void stop(){

if (mTimer != null) {

mTimer.purge();

mTimer.cancel();

mTimer = null;

}

if (mTimeTask != null) {

mFrameIndex = 0;

mState = STATE_STOP;

mTimeTask.cancel();

mTimeTask = null;

}

//移除Handler消息

if (AnimHandler != null) {

AnimHandler.removeMessages(MSG_START);

AnimHandler.removeMessages(MSG_STOP);

AnimHandler.removeCallbacksAndMessages(null);

}

}

/**

* 定时器任务

*/

class AnimTimerTask extends TimerTask {

@Override

public void run() {

if (mFrameIndex < 0 || mState == STATE_STOP) {

return;

}

if (mFrameIndex < mResourceIdList.size()) {

Message msg = AnimHandler.obtainMessage(MSG_START, 0, 0, null);

msg.sendToTarget();

} else {

mFrameIndex = 0;

if(!isLooping){

Message msg = AnimHandler.obtainMessage(MSG_STOP, 0, 0, null);

msg.sendToTarget();

}

}

}

}

/**

* Handler

*/

private Handler AnimHandler = new Handler(){

public void handleMessage(Message msg) {

switch (msg.what) {

case MSG_START:{

if(mFrameIndex >= 0 && mFrameIndex < mResourceIdList.size() && mState == STATE_RUNNING){

mImageView.setImageResource(mResourceIdList.get(mFrameIndex));

mFrameIndex++;

}

}

break;

case MSG_STOP:{

if (mTimeTask != null) {

mFrameIndex = 0;

mTimer.purge();

mTimeTask.cancel();

mState = STATE_STOP;

mTimeTask = null;

if (isLooping) {

mImageView.setImageResource(0);

}

}

}

break;

default:

break;

}

}

};

public interface OnParseListener {

void onParse(List res);

}

}

方式二:

1.定义类XFrameAnimation,继承自Drawable类,同时实现Animatable接口。

2.XFrameAnimation内部通过ValueAnimator(动画的数值发生器)来有序的产生图片资源的resId,然后在自身的draw方法中将resId对应的资源绘制到Canvas上。传入的是一个图片资源数组,所以呈现出来的就是一个帧动画的效果。

3.使用

// 图片资源Id数组

int[] RES_IDS = new int[]{

R.drawable.loading_1840000,

R.drawable.loading_1840001,

......

};

// 构建播放图片的XFrameAnimation

XFrameAnimation loadingDrawable = new XFrameAnimation(600, RES_IDS, getContext().getResources());

ImageView ivLoadingImage = (ImageView) findViewById(R.id.iv_loading_image);

ivLoadingImage.setImageDrawable(loadingDrawable);

4.代码(参考自网上一位大神分享的,具体原链接暂时找不着了,这个代码是之前写的)

public class XFrameAnimation extends Drawable implements Animatable {

private static final long DEFAULT_DURATION = 500;

private long duration = DEFAULT_DURATION;

private final Paint mPaint;

private final int[] RES_IDS;

private int resIndex;

private final Resources mResources;

private ValueAnimator mAnimator;

private ValueAnimator.AnimatorUpdateListener mAnimUpdateListener;

//取第一帧,用于获取图片宽高

private Drawable mFirstDrawable;

public XFrameAnimation(int[] RES_IDS, Resources resources) {

this(DEFAULT_DURATION, RES_IDS, resources);

}

public XFrameAnimation(long duration, int[] RES_IDS, Resources resources) {

this.duration = duration;

this.RES_IDS = RES_IDS;

this.mResources = resources;

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mPaint.setFilterBitmap(true);

mPaint.setDither(true);

if (this.RES_IDS == null || this.RES_IDS.length <= 0) {

throw new RuntimeException(" XFrameAnimation RES_IDS can not null or empty !!!");

}

mFirstDrawable = resources.getDrawable(this.RES_IDS[0]);

createAnimator();

}

/**

* 初始化动画

*/

private void createAnimator() {

mAnimator = ValueAnimator.ofInt(RES_IDS.length - 1);

mAnimator.setInterpolator(new LinearInterpolator());

mAnimator.setRepeatCount(ValueAnimator.INFINITE);

mAnimator.setRepeatMode(ValueAnimator.RESTART);

mAnimator.setDuration(duration);

mAnimUpdateListener = new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

invalidate(((int) animation.getAnimatedValue()));

}

};

}

/**

* 重绘

*

* @param index 帧索引

*/

public void invalidate(int index) {

this.resIndex = index;

invalidateSelf();

}

/**

* 获取动画帧数

*

* @return 帧数量

*/

public int getFrameCount(){

return RES_IDS.length;

}

@Override

public void draw(Canvas canvas) {

if (mResources != null) {

BitmapDrawable drawable = (BitmapDrawable) mResources.getDrawable(RES_IDS[resIndex % RES_IDS.length]);

Bitmap bitmap = drawable.getBitmap();

canvas.drawBitmap(bitmap, 0, 0, mPaint);

}

}

@Override

public void setAlpha(int alpha) {

}

@Override

public void setColorFilter(ColorFilter colorFilter) {

mPaint.setColorFilter(colorFilter);

}

@Override

public int getOpacity() {

return PixelFormat.OPAQUE;

}

@Override

public void start() {

// If the animators has not ended, do nothing.

if (mAnimator.isStarted()) {

return;

}

startAnimator();

invalidateSelf();

}

/**

* 开始执行动画

*/

private void startAnimator() {

if (mAnimator != null) {

mAnimator.addUpdateListener(mAnimUpdateListener);

mAnimator.start();

}

}

@Override

public void stop() {

if (mAnimator != null && mAnimator.isStarted()) {

mAnimator.removeAllUpdateListeners();

mAnimator.end();

}

}

@Override

public boolean isRunning() {

return mAnimator.isRunning();

}

@Override

public int getIntrinsicWidth() {

return mFirstDrawable.getIntrinsicWidth();

}

@Override

public int getIntrinsicHeight() {

return mFirstDrawable.getIntrinsicHeight();

}

}

android 自定义帧动画,Android 自定义方式实现帧动画效果相关推荐

  1. 关卡 动画 蓝图 运行_UE4无缝过场动画

    最近有个哥们给我看他们最近在做的一个游戏,其中有这样一段镜头 https://www.zhihu.com/video/1171378736917364736 运用到了一个很常用的过场方式,就是平时我们 ...

  2. android 播放gif动画效果,android 通过帧动画方式播放Gif动画

    注意:经过本人测试,这个方法很耗内存, 图片一多就崩了.慎用 <1>用工具(photoshop或者FireWorks)将GIF动画图片分解成多个GIF静态图片,然后保存在res\drawa ...

  3. android覆盖扩散动画,[Android]多层波纹扩散动画——自定义View绘制

    之前整理过一些属性动画的基本操作,这一段时间的动画相关需求都安然度过了.直到这次-- 一.另一种动画需求 多数交互中的动画都是让单个页面元素动起来,这种就很适合用属性动画实现.但是对于 多个元素.非页 ...

  4. android+清除循环动画,android自定义View之(4)-一键清除动画

    android自定义View之(四)------一键清除动画 1.前言: 自己也是参考别人的一些自定义view例子,学习了一些基本的自定义view的方法.今天,我参考了一些资料,再结合自已的一些理解, ...

  5. android自定义过渡动画,11.自定义过渡动画

    11.1 问题 应用程序需要自定义Activity切换或Fragment切换时产生的过渡动画. 11.2 解决方案 (API Level 5) 要修改Activity间的过渡动画,可以使用overri ...

  6. android 自定义园动画,Android动画:一个等待动画的制作过程

    看到一个很好玩的gif等待动画,记录一下制作过程. 先上图,展示一下这gif. 图中四个空心圆,一个实心园,依次作规则双星运动. 三个晚上,目前已经已经实现了.又学到了不少东西,这几天把博客写完. 放 ...

  7. Android属性动画与自定义View——实现vivo x6更新系统的动画效果

    晚上好,现在是凌晨两点半,然后我还在写代码.电脑里播放着<凌晨两点半>,晚上写代码,脑子更清醒,思路更清晰. 今天聊聊属性动画和自定义View搭配使用,前面都讲到自定义View和属性动画, ...

  8. android卡包动画,自定义View实现银行卡卡包动画效果

    本来不想自己造轮子的,但奈何没找动相应效果的轮子,所以只能自己写了,其实还是白嫖来的轻松,哈哈 先看效果 这个是完成的效果,还可以吧!关键也不难一个自定义View搞定 先说一下思路,继承一个Relat ...

  9. android 自定义ViewGroup和对view进行切图动画实现滑动菜单SlidingMenu[转]

    http://blog.csdn.net/jj120522/article/details/8095852 示意图就不展示了,和上一节的一样,滑动菜单SlidingMenu效果如何大家都比较熟悉,在这 ...

最新文章

  1. [PaaS] 深入 Cloud Foundry(一)构架 (转载)
  2. php调用c windows,php:在WINDOWS中设置计划任务执行PHP文件_PHP教程
  3. 装机之windows10和ubuntu双系统
  4. Strus2第一次课:dom4j操作xml
  5. 去哪儿-17-detail-header
  6. 学习记录012-NFS
  7. ES6 Promise - 让我们解开的面纱(遵循Promise/A+规范)
  8. Swift - 项目部署配置(支持的系统,设备和状态条样式等)
  9. TensorFlow游乐场及神经网络简介
  10. 计算机理工 教学计划,教学计划格式-华南理工大学计算机科学与工程学院.DOC
  11. 使用Sqlite数据库存储数据
  12. 用粉红噪声煲机_煲机知识 | 煲机常用的粉红噪音和白噪音是什么?
  13. 2021-10-11 全国大学生软件测试大赛赛前学习参考资料
  14. (运存扩展器)Android手机内存扩展软件RAMEXPANDER使用教程
  15. c语言程序设计教程2014版,C语言程序设计案例教程(第3版)
  16. sxt_1_struts2入门_hello
  17. python爬虫实践之下载轻音乐
  18. python一般用几个空格表示缩进_Python 就是使用缩进来表示代码块,一般使用几个空格来表示一个缩进_女子礼仪答案_学小易找答案...
  19. Atcoder TOYOTA SYSTEMS Programming Contest 2021(AtCoder Beginner Contest 228) C - Final Day
  20. 360公司2016校园招聘笔试题大题一

热门文章

  1. MFC的Serialize机制及其使用(转)
  2. vue-cli3中安装lib-flexible和px2rem
  3. matlab求系统根轨迹代码_要想正确画出根轨迹,先搞清楚这8大法则再说!
  4. sendkeys鼠标点击_selenium操作详解之鼠标键盘事件
  5. osg中三维模型的位置变换
  6. 2020河南工业大学计算机考研科目,你知道2020年河南工业大学硕士研究生考研有哪些复试科目...
  7. oracle end backup,oracle-backup-hot backup
  8. 室内装修隐蔽工程验收知识拓展_装修之前先看知贤,『装修微课堂』室内装修隐蔽工程详解,装修小白防坑避雷手册!...
  9. newduba首页怎么去掉_京喜小程序首页瘦身实践
  10. 索泰显卡超频软件测试要多少时间,索泰显卡专用超频软件_FireStorm显卡超频 V2.0.1 官方版...