Android属性动画优化(更高效的使用属性动画)
属性动画的基础知识可以上郭霖的blog补下。地址:http://blog.csdn.net/guolin_blog/article/details/43536355
以下是自己另附的一些优化策略。
1.使用PropertyValuesHolder
想必大家都这样写过:
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);AnimatorSet animSetXY = new AnimatorSet();animSetXY.playTogether(animX, animY);animSetXY.start();
但是这样写会产生两个ObjectAnimator对象,效率较低,官方建立这样写:
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
这里使用了PropertyValuesHolder,只产生一个ObjectAnimator对象,更加高效。
一个view同时发生多种效果时,建议这种写法。
2.使用Keyframe
同样,我们肯定也这样写过:
AnimatorSet animSet = new AnimatorSet();ObjectAnimator transYFirstAnim = ObjectAnimator.ofFloat(mView, "translationY", 0, 100);ObjectAnimator transYSecondAnim = ObjectAnimator.ofFloat(mView, "translationY", 100, 0);animSet.playSequentially(transYFirstAnim, transYSecondAnim);
产生两个ObjectAnimator对象,一个AnimatorSet,代码繁琐,对象冗杂。
这种情况建议使用Keyframe编写其行为。从词义上来理解Keyframe是关键帧,下面是使用关键帧的例子:
Keyframe k0 = Keyframe.ofFloat(0f, 0); //第一个参数为“何时”,第二个参数为“何地”Keyframe k1 = Keyframe.ofFloat(0.5f, 100);Keyframe k2 = Keyframe.ofFloat(1f, 0);PropertyValuesHolder p = PropertyValuesHolder.ofKeyframe("translationY", k0, k1, k2);ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mView, p);objectAnimator.start();
所以效果就是:
开始时 位置为0;
动画开始1/2时 位置为100;
动画结束时 位置为0。
一个view的单个属性先后发生一系列变化时,建议使用Keyframe达到效果。
总结就是,如果是同一个view的一系列动画,均可使用以上组合方式达到只使用一个ObjectAnimator的效果 。多个view的动画用AnimatorSet进行动画组合和排序,代码架构和执行效率基本能达到最优化。
3.使用AnimatorListenerAdapter代替AnimatorListener
由于AnimatorListener是接口,所以实现它得实现它所有的方法,而我们有时只用到它的个别回调(如:onAnimationStart),使用它会导致代码看起来非常冗杂。而AnimatorListenerAdapter是默认实现了AnimatorListener的一个抽象类,你可以按需要重写其中的方法,代码会优雅一点。
4.使用ViewPropertyAnimator
属性动画的机制已经不是再针对于View而进行设计的了,而是一种不断地对值进行操作的机制,它可以将值赋值到指定对象的指定属性上。
但是,在绝大多数情况下,还是对View进行动画操作的。Android开发团队也是意识到了这一点,于是在Android 3.1系统当中补充了ViewPropertyAnimator这个机制。
ObjectAnimator animator = ObjectAnimator.ofFloat(textview, “alpha”, 0f); animator.start();
等同
textview.animate().alpha(0f);
animate()方法就是在Android 3.1系统上新增的一个方法,这个方法的返回值是一个ViewPropertyAnimator对象。(简明方便)
textview.animate().x(500).y(500).setDuration(5000).setInterpolator(new BounceInterpolator());
5.属性动画其他一些实用方法:(暂时只讲了API 14—Android 4.0及以下的部分)
setStartDelay():可以设置动画开始前的延迟时间。注意:此方法API 14加入;如果给动画添加了AnimatorListener,Listener的onAnimationStart方法会在动画最开始的时候回调,而不是delay一段时间后回调。
isStarted()(API 14)与isRunning():这个得与setStartDelay()放在一起讲,也就是说,当动画在delay中并没有真正开始时,isStarted返回false,而isRunning返回true。也就是,isRunning的周期是大于isStarted的。
cancel()与end():cancel方法会立即停止动画,并且停留在当前帧。end方法会立即停止动画,并且将动画迅速置到最后一帧的状态。
setupStartValues()与setupEndValues():设置动画进行到的当前值为开始值(结束值)。示例如下:
以下这个示例中,当view移动了2.5s的时候,cancel当前动画,并重新设定起始位置为当前位置,结束位置为400,再开始此动画。
final ObjectAnimator o = ObjectAnimator.ofFloat(mTextView, "x", 0, 500);o.setDuration(5000);o.start();mTextView.postDelayed(new Runnable() { @Override public void run() { if (o.isRunning()) { o.cancel(); } o.setupStartValues(); o.setFloatValues(400); o.start(); }},2500);
6.做一个属性动画的方式:(摘自Android开发艺术探索)
1.给你的对象加上get和set方法,前提是你有权限。
2.用一个类来包装原始对象,间接为其提供get和set方法。
/** * 用一个类来包装原始对象,间接为其提供get和set方法。 */ private static class ViewWrapper { private View mTarget; public ViewWrapper(View target) { mTarget = target; } public int getWidth() { return mTarget.getLayoutParams().width; } public void setWidth(int width) { mTarget.getLayoutParams().width = width; mTarget.requestLayout(); } } /** * TextView不能直接设置width,这里即采用了包装的方式,对textview的width进行属性动画 */ private void animViewWidthByWidth() { ViewWrapper wrapper = new ViewWrapper(mTextView); ObjectAnimator widthAnim = ObjectAnimator.ofObject(wrapper, "width", new IntEvaluator(), mTextView2.getWidth(), mTextView2.getWidth() * 2); widthAnim.setDuration(1000); widthAnim.setRepeatCount(100); widthAnim.start(); }
3.采用ValueAnimator,监听动画过程,自己实现属性的改变。
ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);anim.setDuration(300);anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentValue = (float) animation.getAnimatedValue(); //此处即可根据currentValue的值自己监听过程,实现计算。 Log.d("TAG", "cuurent value is " + currentValue); }});anim.start();
7.哪些线程能做属性动画
属性动画的start()方法,首先就有判定:
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } ......}
说明,需要有Looper的线程才可以做属性动画。很明显,Android原生环境中,主线程和HandlerThread是可以做属性动画的,当然,也可以自己实现一个有Looper的Thread。
8.属性动画的内存泄露问题
许多时候用到了无限循环的动画,我们会这样写:animator.setRepeatCount(ValueAnimator.INFINITE);
这样写如果没有及时取消,会导致此属性动画持有被动画对象的引用而导致内存泄露,故在activity生命周期结束时,如onDestroy方法中,及时的cancel掉这种动画。
补间动画无此问题,它是给view做动画,在view的onDetachedFromWindow方法会取消掉动画,所以不会导致内存泄露。
更多相关文章
- Android SVG矢量资源的使用方法
- android图片压缩的3种方法实例
- Android Selector和Shape的使用方法
- android内核编译方法
- Android矢量动画-VectorDrawable
- APP开发实战85-帧动画
- 实现android启动界面字体的动画效果