安卓基础-动画机制和使用技巧

Android View动画框架

Animation框架定义了透明度、旋转、缩放和位移几种常见的动画,控制的是整个view,原理是每次绘制视图时view所在的viewgroup中的drawchild函数获取该view的animation的transformation值,然后调用canvas.concat(transformToApply.getMatrix()),通过矩阵换算完成动画帧,如果动画没有完成,就继续调用invalidate()函数,启动下次绘制来驱动动画,从而完成整个动画的绘制。

视图动画

视图动画使用简单,效果丰富,提供了四种方式,并提供了animationset动画集合,混合使用多种动画。

  1. 透明度动画

为视图增加透明度的变换动画。

1
2
3
AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
view.startAnimation(aa);
  1. 旋转动画

为视图增加旋转的变换动画。

1
2
3
RotateAnimation ra = new RotateAnimation(0, 360, 100, 100);
ra.setDuration(1000);
view.startAnimation(ra);

也可以以自身为旋转中心

1
2
3
RotateAnimation ra = new RotateAnimation(0, 360,
RotateAnimation.RELATIVE_TO_SELF, 0.5F,
RotateAnimation.RELATIVE_TO_SELF, 0.5F);
  1. 位移动画

为视图移动时增加位移动画

1
2
3
TranslationAnimation ta = new TranslationAnimation(0, 200, 0, 300);
ta.setDuration(1000);
view.startAnimation(ta);
  1. 缩放动画

为视图的缩放增加动画效果

1
2
3
ScaleAnimation sa = new ScaleAnimation(0, 2, 0, 2);
sa.setDuration(1000);
view.startAnimation(sa);

也可以以自身为缩放中心

1
2
3
4
5
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1,
Animation.RELATIVE_TO_SELF, 0.5F,
Animation.RELATIVE_TO_SELF, 0.5F);
sa.setDuration(1000);
view.startAnimation(sa);
  1. 动画合集

使用AnimationSet,可以将动画以组合的形式展现出来。

1
2
3
4
5
6
7
8
9
10
11
12
AnimationSet as = new AnimationSet(true);
as.setDuration(1000);

AlphaAnimation aa = new AlphaAnimation(0, 1);
aa.setDuration(1000);
as.addAnimation(aa);

TranslateAnimation ta = new TranslateAnimation(0, 100, 0, 200);
ta.setDuration(1000);
as.addAnimation(ta);

view.startAnimation(as);

属性动画

由于视图动画改变的只是view的显示,而并不是view的属性,会导致一些问题。

  1. ObjectAnimator
    

ObjectAnimator是属性动画框架中最重要的实体类,创建一个ObjectAnimator只需要通过他的静态工厂类直接返回一个ObjectAnimator对象。参数包括一个对象和对象的属性名字,但这个属性必须有get和set函数,内部会通过java反射机制来调用set函数修改对象属性值。

1
2
3
4
ObjectAnimator animator = ObjectAnimator.ofFloat(
view, "translationX", 300);
animator.setDuration(300);
animator.start();

第一个参数为要操纵的view,第二个参数为要操纵的属性,第三个三处为一个可变数组参数,需要传进该属性变化的一个取值过程。

如果需要操控一个值,但是该值没有setget方法,可以使用wrapper方法来进行一层包装。

1
2
3
4
5
6
7
8
9
10
11
12
13
private static class WrapperView{
private View mTarget;
public WrapperView(View target){
mTarget = target;
}
public int getWidth(){
return mTarget.getLayoutParams().width;
}
public void setWidth(int width){
mTarget.getLayoutParams().width = width;
mTarget.requestLayout();
}
}
1
2
ViewWrapper wrapper = new ViewWrapper(mButton);
ObjectAnimator.ofInt(wrapper, "width", 500).setDuration(5000).start();
  1. PropertyValuesHolder

类似视图动画中的AnimationSet,在属性动画中,如果针对同一个对象的多个属性,要同时作用多种动画,可以使用PropertyValuesHolder来实现。

1
2
3
4
PropertyValuesHolder pvh1 = PropertyValuesHolder.ofFloat("translationX", 300f);
PropertyValuesHolder pvh2 = PropertyValuesHolder.ofFloat("scaleX", 1f, 0, 1f);
PropertyValuesHolder pvh3 = PropertyValuesHolder.ofFloat("scaleY", 1f, 0, 1f);
ObjectAnimator.ofPropertyValuesHolder(view, pvh1, pvh2, pvh3).setDuration(1000).start();
  1. ValueAnimator

ValueAnimator本身不提供任何动画效果,它更像一个数值发生器,用来产生具有一定规律的数字,从而让调用者来控制动画的实现过程。

1
2
3
4
5
6
7
8
9
10
ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
animator.setTarget(view);
animator.setDuration(1000).start();
animator.addUpdateListener(new AnimatorUpdateListener(){
@Override
public void onAnimationUpdate(ValueAnimator animation){
Float value = (Float) animation.getAnimatedValue();
//TODO use the value
}
})
  1. 动画事件的监听

一个完整的动画具有Start, Repeat, End, Cancel四个过程,对动画使用AddListener()即可很方便的监听到这四个事件。

  1. AnimatorSet

对于一个属性同时作用多个属性动画效果吗可以使用之前的PrpertyValuesHolder实现,而AnimatorSet不仅可以实现效果,还能控制顺序。

1
2
3
4
5
6
7
ObjectAnimator animator1 = ObjectAnimator.ofFloat(view, "translationX", 300f);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0f, 1f);
ObjectAnimator animator3 = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0f, 1f);
AnimatorSet set = new AnimatorSet();
set.setDuration(1000);
set.playTogether(animator1, animator2, animator3);
set.start();

使用playTogether(), playSequentially(), animSet.play().with(),before(),after()来控制多个动画的协同工作方式。

  1. 在xml中使用属性动画
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:duration="1000"
Android:propertyName="scaleX"
Android:valueFrom="1.0"
Android:valueTo="2.0"
Android:valueType="floatType">
</objectAnimator>
1
2
3
4
5
public void scaleX(View view){
Animator anim = AnimatorInflater.loadAnimator(this, R.animator.scalex);
anim.setTarget(mMv);
anim.start();
}
  1. View的animate方法

animate方法是属性动画的一种简写方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
view.animate()
.alpha(0)
.y(300)
.setDuration(300)
.withStartAction(new Runnable(){
@Override
public void run(){

}
})
.withEndAction(new Runnable(){
@Override
public void run(){

}
}).start();

布局动画

布局动画是指在ViewGroup上,给ViewGroup添加view时添加一个动画过度效果。

最简单的布局动画,只需要添加

1
Android:animateLayoutChanges="true"

另外,还可以通过使用LayoutAnimationController类来自定义一个子View的过度效果。

1
2
3
4
5
6
7
8
9
LinearLayout ll = (LinearLayout)findViewById(R.id.ll);
//设置过渡动画
ScaleAnimation sa = new ScaleAnimation(0, 1, 0, 1);
sa.setDuration(2000);
//设置布局动画的显示属性
LayoutAnimationController lac = new LayoutAnimationController(sa, 0.5F);
lac.setOrder(LayoutAnimationController.ORDER_NORMAL);
//为viewgroup设置布局动画
ll.setLayoutAnimation(lac);

上述代码可以使得子view出现时有一个缩放的效果。

插值器

插值器用于定义动画变化速率。

自定义动画

自定义动画是继承一个Animation然后自己写,自定义动画可以获得当前矩阵对象,然后对矩阵进行操作变化,可以实现任何效果的动画。