您的位置:首页 > 移动开发 > Android开发

Android属性动画优化(更高效的使用属性动画)

2018-01-22 09:56 711 查看
属性动画的基础知识可以上郭霖的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();
1
2
3
4
5
但是这样写会产生两个ObjectAnimator对象,效率较低,官方建立这样写:

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);

ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvyY).start();
1
2
3
4
这里使用了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);
1
2
3
4
5
6
7
产生两个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();
1
2
3
4
5
6
7
8
所以效果就是:

开始时 位置为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);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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();
1
2
3
4
5
6
7
8
9
10
11
7.哪些线程能做属性动画

属性动画的start()方法,首先就有判定:

private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
......
}
1
2
3
4
5
6
说明,需要有Looper的线程才可以做属性动画。很明显,Android原生环境中,主线程和HandlerThread是可以做属性动画的,当然,也可以自己实现一个有Looper的Thread。

8.属性动画的内存泄露问题

许多时候用到了无限循环的动画,我们会这样写:animator.setRepeatCount(ValueAnimator.INFINITE);

这样写如果没有及时取消,会导致此属性动画持有被动画对象的引用而导致内存泄露,故在activity生命周期结束时,如onDestroy方法中,及时的cancel掉这种动画。

补间动画无此问题,它是给view做动画,在view的onDetachedFromWindow方法会取消掉动画,所以不会导致内存泄露。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: