前言:来自菜鸟程序员的第二篇博客,记录一个可以防止OOM的帧动画开发,方便后面需要用到时复制粘贴。
开发背景:项目要求加上动画显示,UI小姐姐直接给我切了两百多张帧动画图片。好吧,之前看过《第一行代码》和《疯狂Android》,知道可以直接用AnimationDrawable来开发逐帧动画,那就直接开干把,于是改好了那200多张图片的名字,写好了xml文件,兴高采烈地运行程序!……纳尼,java.lang.OutOfMemoryError……一脸懵逼啊!问了旁边的一个同事,他告诉我说图片太多了,内存溢出,然后发给我一个class,说你看一下这个吧,用这个不管有多少图片都不会内存溢出。然后我看了一下,三百多行代码,一个注释都没有(最讨厌看那些一个注释都没有的代码了)。不过也没办法了,好好研究一下吧,里里外外研究并测试了半天给相关的地方加上了注释后,发现好多都可以简化一下,并且简化后也没有出错,瞬间只剩下不到一百行代码了,这里就记录一下简化后的代码。
一、帧动画类
其实这里最关键的就是在play()函数中直接调用了ImageView.postDelayed()方法并在该方法中调用play()函数自己(递归)来实现不停的替换图片。
package com.example.view;
import android.widget.ImageView;
/**
* 帧动画工具类,防止OOM
*/
public class FrameAnimation {
/** 是否循环播放 */
private boolean mIsRepeat;
/** 播放动画的控件 */
private ImageView mImageView;
/** 播放的图片数组 */
private int[] mFrameRess;
/** 每帧动画的播放间隔 */
private int mDuration;
/** 最后一帧图片 */
private int mLastFrame;
/** 暂停 */
private boolean mPause = true;
/** 当前帧 */
private int mCurrentFrame = 0;
/**
* @param iv
* 播放动画的控件
* @param frameRes
* 播放的图片数组
* @param duration
* 每帧动画的播放间隔(毫秒)
* @param isRepeat
* 是否循环播放
*/
public FrameAnimation(ImageView iv, int[] frameRes, int duration,
boolean isRepeat) {
this.mImageView = iv;
this.mFrameRess = frameRes;
this.mDuration = duration;
this.mLastFrame = frameRes.length - 1;
this.mIsRepeat = isRepeat;
}
/** 播放动画 */
private void play(final int i) {
mImageView.postDelayed(new Runnable() {
@Override
public void run() {
if (mPause) {
mCurrentFrame = i;
return;
}
mImageView.setImageResource(mFrameRess[i]);
if (i == mLastFrame) {
if (mIsRepeat) {
play(0);
}
} else {
play(i + 1);
}
}
}, mDuration);
}
/** 停止播放 */
public void stopAnimation() {
this.mPause = true;
}
/** 是否暂停 */
public boolean isPause() {
return this.mPause;
}
/** 播放动画 */
public void restartAnimation() {
if (mPause) {
mPause = false;
play(mCurrentFrame);
}
}
/** 关闭动画 */
public void cancle() {
if (mImageView != null) {
stopAnimation();
mImageView.removeCallbacks(null);
}
}
}
二、activity_main.xml布局文件
测试所用,简简单单,一个ImageView用来显示动画,一个Button用来控制动画的暂停和播放。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<ImageView
android:id="@+id/anim_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"/>
<Button
android:id="@+id/pause_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放/暂停"
android:layout_below="@id/anim_view"
android:layout_centerHorizontal="true"
android:layout_marginTop="50px"
/>
</RelativeLayout>
三、array_hand_anim.xml文件
在values里添加图片资源组array_hand_anim.xml,并且把相应的图片放到drawable-hdpi里去。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="hand_anim_array">
<item> @drawable/hand02 </item>
<item> @drawable/hand04 </item>
<item> @drawable/hand06 </item>
<item> @drawable/hand08 </item>
<item> @drawable/hand10 </item>
<item> @drawable/hand12 </item>
<item> @drawable/hand14 </item>
<item> @drawable/hand16 </item>
<item> @drawable/hand18 </item>
<item> @drawable/hand20 </item>
<item> @drawable/hand22 </item>
<item> @drawable/hand24 </item>
<item> @drawable/hand26 </item>
<item> @drawable/hand28 </item>
<item> @drawable/hand30 </item>
<item> @drawable/hand32 </item>
<item> @drawable/hand34 </item>
<item> @drawable/hand36 </item>
<item> @drawable/hand38 </item>
<item> @drawable/hand40 </item>
<item> @drawable/hand42 </item>
<item> @drawable/hand44 </item>
<item> @drawable/hand46 </item>
</array>
</resources>
四、MainActivity文件
这里调用帧动画类,调用的时候需要把资源图片的ID以数组的形式传给帧动画类,所以通过TypedArray来获取资源图片的ID,然后通过一个按钮控制动画的播放和暂停。
package com.example.testiev;
import com.example.view.FrameAnimation;
import android.app.Activity;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
public class MainActivity extends Activity {
/** 动效 */
private FrameAnimation mAnimation;
/** 动效View */
private ImageView anim_view;
/** 暂停/播放动画 */
private Button pause_anim;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
anim_view = (ImageView) findViewById(R.id.anim_view);
mAnimation = new FrameAnimation(anim_view,
getRes(R.array.hand_anim_array), 80, true);
mAnimation.restartAnimation();// 开始动画
pause_anim = (Button) findViewById(R.id.pause_anim);
pause_anim.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mAnimation.isPause()) {
mAnimation.restartAnimation();// 开始动画
} else {
mAnimation.stopAnimation();// 关闭动效
}
}
});
}
/** 获取需要播放的动画资源 */
private int[] getRes(int id) {
TypedArray typedArray = getResources().obtainTypedArray(id);
int len = typedArray.length();
int[] resId = new int[len];
for (int i = 0; i < len; i++) {
resId[i] = typedArray.getResourceId(i, 0);
}
typedArray.recycle();
return resId;
}
}
到此一个可以防止OOM的帧动画程序就完成了,下次要用到直接打开复制粘贴,舒服!