android 自定义帧动画,Android 自定义方式实现帧动画效果

前言

首先说下为啥要通过自定义处理的方式去实现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();

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值