帧动画布局文件
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item android:drawable="@drawable/pear_pic" android:duration="200"/>
<item android:drawable="@drawable/apple_pic" android:duration="200"/>
<item android:drawable="@drawable/banana_pic" android:duration="200"/>
<item android:drawable="@drawable/orange_pic" android:duration="200"/>
<item android:drawable="@drawable/mango_pic" android:duration="200"/>
<item android:drawable="@drawable/mango_pic" android:duration="200"/>
</animation-list>
代码文件
class TestAnimatorActivity : AppCompatActivity() {
val TAG = TestAnimatorActivity::class.java.simpleName
//从xml中获取布局文件和view
/*override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test_animator)
//代码方式给imageview设置背景
//image.setBackgroundResource(R.drawable.framelist)
val background = image.background
play.setOnClickListener {
//开始播放帧动画前,先暂停。否则只有第一次点击生效
(background as AnimationDrawable).stop()
(background as AnimationDrawable).start()
}
stop.setOnClickListener {
(background as AnimationDrawable).stop()
}
}*/
//代码方式创建布局和view
lateinit var myView: AnimationDrawableImageView
var num = 0
var handler = object : Handler() {
/**
* Subclasses must implement this to receive messages.
*/
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what) {
1 -> {
myView.visibility = View.INVISIBLE
}
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var frame = FrameLayout(this)
setContentView(frame)
myView = AnimationDrawableImageView(this)
myView.setBackgroundResource(R.drawable.framelist)
myView.visibility = View.INVISIBLE
var background = myView.background as AnimationDrawable
myView.setDrawable(background)
frame.addView(myView)
frame.setOnTouchListener { v, event ->
if (event.action == ACTION_DOWN) {
Log.e(TAG, "onCreate: ${num++}")
myView.setLocation(event.y.toInt(), event.x.toInt())
var background = myView.background as AnimationDrawable
myView.visibility = View.VISIBLE
background.stop()
background.start()
/*Thread(Runnable {
Thread.sleep(1000)
handler.sendEmptyMessage(1)
}).start()*/
//方案1:开启子线程,休眠,然后通过handler让主线程操作UI
/*thread {
Thread.sleep((background.numberOfFrames * background.getDuration(0)).toLong())
handler.sendEmptyMessage(1)
}*/
//方案2:直接调用view的runOnUiThread()方法,不需要借助handler
/*thread {
Thread.sleep((background.numberOfFrames * background.getDuration(0)).toLong())
runOnUiThread { myView.visibility = View.INVISIBLE }
}*/
//方案3 使用view的postDelay()
/*thread {
Log.e(TAG, "onSubThread liuliu ")
*//*myView.postDelayed( {
Log.e(TAG, "onUIThread liuliu ")
myView.visibility = View.INVISIBLE },
(background.numberOfFrames * background.getDuration(0)).toLong())*//*
myView.postDelayed( {
Log.e(TAG, "onUIThread liuliu ")
myView.visibility = View.INVISIBLE }, 5 * 60 * 1000)
}*/
//方案4:直接使用view的postdelayed
/*myView.postDelayed( {
Log.e(TAG, "onUIThread liuliu ")
myView.visibility = View.INVISIBLE }, 5 * 60 * 1000)*/
}
return@setOnTouchListener true
}
}
}
自定义view
public class AnimationDrawableImageView extends AppCompatImageView {
private String TAG = AnimationDrawableImageView.class.getSimpleName();
private AnimationDrawable mDrawable;
private int num = 0;
public AnimationDrawableImageView(Context context) {
super(context);
}
public void setLocation(int top, int left) {
this.setFrame(left, top, left + 100, top + 100);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.e(TAG, "onDraw: " + ++num );
try {
Field filed = AnimationDrawable.class.getDeclaredField("mCurFrame");
filed.setAccessible(true);
int curFrame = filed.getInt(mDrawable);
if (curFrame == mDrawable.getNumberOfFrames() - 1) {
setVisibility(View.INVISIBLE);
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public void setDrawable(AnimationDrawable mDrawable) {
this.mDrawable = mDrawable;
}
}
总结一下:
1、帧动画可以在xml文件定义,xml资源文件的头标签是<animation-list>。也可以在java代码中通过AnimationDrawable对象的addFrame(Drawable frame, int duration)来向动画中添加帧。
2、帧动画资源通常作为imageview的背景使用,可以利用background属性在xml中给imageview指定动画资源,也可以通过代码image.setBackgroundResource(int resId)来指定动画资源id;
3、帧动画默认是不会自动执行的,需要通过AnimationDrawable的start()和stop()来控制帧动画的播放与暂停。注意调用start()前需要先调用下stop()。
4、在TestAnimatorActivity类中我们看到,我们可以通过java代码的方式创建一个布局对象FrameLayout,然后再通过actiivity的setContentView()来添加并显示布局到activity中。这和使用R.layout.xxx方式其实是一样的。
5、在TestAnimatorActivity类中, 也是通过java代码的方式创建一个ImageView的子类对象(这其实也可以在xml文件中使用,然后通过R.id.xxx在代码中引用)。然后调用了layout对象(本质是一个ViewGroup)的addView(view)方法把view添加到布局中。 这和xml文件中,直接在布局文件中添加view标签是一样的。
6、实现在屏幕上点击一下,就出现帧动画。选择给布局对象添加触摸监听器setOnTouchListener,在OnTouchListener中启动帧动画。
这里有一点需要注意,在OnTouchListener的onTouch(View v, MotionEvent event)方法中,MotionEvent常见的有MotionEvent.ACTION_DOWN、MotionEvent.ACTION_UP和MotionEvent.MOVE。每个事件发生是都会执行到onTouch(View v, MotionEvent event)方法中的逻辑。计算我们最简单的点击屏幕然后马上抬起手指,就会触发MotionEvent.ACTION_DOWN-->MotionEvent.ACTION_UP。在onTouch()方法中需要对事件过滤一下,只在ACTION_DOWN中开启动画。 这样可以避免开启、关闭无意义的动画。
7、设置imageview的大小setFrame()函数(这是View基类上的方法)。设置view的两个对角点的坐标,确定view的位置和大小尺寸。
protected boolean setFrame(int left, int top, int right, int bottom)
8、解决帧动画播放完之后,imageview在布局中隐藏功能
方案一:在ImageView的OnDraw()中判断当前播放的是不是动画最后一帧,是的话就影藏imageview。
首先需要知道的是:帧动画资源作为imageview的背景时,帧动画每播放一帧,就会调用一次imageview的OnDraw()函数进行绘制当前帧到imageview中作为背景。
自定义了一个AnimationDrawableImageView,并重写了OnDraw()方法。在其内部通过反射的方式获取到当前帧动画对象正在播放的是第几帧(之所以需要使用反射是因为AnimationDrawable中的mCurFrame属性是私有的)。当播放的为帧动画的最后一帧时,执行setVisibility(View.INVISIBLE)隐藏imageview。----当某个对象的属性是私有的时候,我们可以通过反射方式获取其属性。
方案二、通过计算帧动画所有帧耗时时间得出总耗时,在这时间之后把imageview设置为隐藏
可以使用子线,或直接调用view.potsDelayed()函数进行延时操作。 写法很多,下面展示几个
//方案1:开启子线程,休眠,然后通过handler让主线程操作UI
thread {
Thread.sleep((background.numberOfFrames * background.getDuration(0)).toLong())
handler.sendEmptyMessage(1)
}
//方案2:直接调用view的runOnUiThread()方法,不需要借助handler
thread {
Thread.sleep((background.numberOfFrames * background.getDuration(0)).toLong())
runOnUiThread { myView.visibility = View.INVISIBLE }
}
//方案3 使用view的postDelay()
thread {
myView.postDelayed( {
Log.e(TAG, "onUIThread liuliu ")
myView.visibility = View.INVISIBLE },
(background.numberOfFrames * background.getDuration(0)).toLong())
}
//方案4:直接使用view的postdelayed
myView.postDelayed( {
Log.e(TAG, "onUIThread liuliu ")
myView.visibility = View.INVISIBLE }, 5 * 60 * 1000)