android 自定义帧动画,Android 自定义方式实现帧动画效果
前言
首先說下為啥要通過自定義處理的方式去實現(xiàn)Android的幀動畫效果,因為通過系統(tǒng)原生支持的xml和java代碼這兩種方式實現(xiàn),在播放的圖片量很多時,會出現(xiàn)內(nèi)存溢出,此現(xiàn)象也是在做項目當中有遇到,出現(xiàn)的情景:loading視圖,由于項目中的加載視圖采用的是播放一組連續(xù)圖片來實現(xiàn)動畫效果。殊不知這樣做是有隱患的,那就是造成了大名鼎鼎的OOM。經(jīng)過幾番折騰和各種嘗試,最終還是決定放棄原來幀動畫實現(xiàn)方式,另辟蹊徑。
方式一:
1.定義類XAnimationDrawable,在內(nèi)部采用定時器給ImageView設(shè)置圖片。
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)設(shè)置播放的圖片資源
//通過代碼添加圖片id資源對應(yīng)的播放動畫方式
frameAnimation.setAnimation(iv, ids);
//通過xml定義圖片id資源列表對應(yīng)的播放動畫方式
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;
//運行狀態(tài)
private int mState = STATE_RUNNING;
//顯示圖片的View
private ImageView mImageView = null;
//圖片資源的ID列表
private List mResourceIdList = null;
//定時任務(wù)器
private Timer mTimer = null;
//定時任務(wù)
private AnimTimerTask mTimeTask = null;
//記錄播放位置
private int mFrameIndex = 0;
//播放形式
private boolean isLooping = false;
public XAnimationDrawable() {
mTimer = new Timer();
}
/**
* 設(shè)置動畫播放資源
*/
public void setAnimation(ImageView imageview, List resourceIdList){
mImageView = imageview;
mResourceIdList = new ArrayList();
mResourceIdList.clear();
mResourceIdList.addAll(resourceIdList);
}
/**
* 設(shè)置動畫播放資源
*/
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 是否循環(huán)播放
* @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);
}
}
/**
* 定時器任務(wù)
*/
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類,同時實現(xiàn)Animatable接口。
2.XFrameAnimation內(nèi)部通過ValueAnimator(動畫的數(shù)值發(fā)生器)來有序的產(chǎn)生圖片資源的resId,然后在自身的draw方法中將resId對應(yīng)的資源繪制到Canvas上。傳入的是一個圖片資源數(shù)組,所以呈現(xiàn)出來的就是一個幀動畫的效果。
3.使用
// 圖片資源Id數(shù)組
int[] RES_IDS = new int[]{
R.drawable.loading_1840000,
R.drawable.loading_1840001,
......
};
// 構(gòu)建播放圖片的XFrameAnimation
XFrameAnimation loadingDrawable = new XFrameAnimation(600, RES_IDS, getContext().getResources());
ImageView ivLoadingImage = (ImageView) findViewById(R.id.iv_loading_image);
ivLoadingImage.setImageDrawable(loadingDrawable);
4.代碼(參考自網(wǎng)上一位大神分享的,具體原鏈接暫時找不著了,這個代碼是之前寫的)
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();
}
/**
* 獲取動畫幀數(shù)
*
* @return 幀數(shù)量
*/
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();
}
/**
* 開始執(zhí)行動畫
*/
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();
}
}
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的android 自定义帧动画,Android 自定义方式实现帧动画效果的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学名言
- 下一篇: Helm中Tiller镜像下载失败的解决