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

属性动画的简单使用和总结

2016-01-27 15:59 561 查看
推荐一本书—《android开发艺术探索》,内容很好很强大,这里也算是个读书笔记吧,在此膜拜一下作者刚哥,期待刚哥的配套视频。。。

在API版本11的时候,andriod新引进了一个新的动画:属性动画,相比之前的View动画,属性动画功能强大,它可以对任何对象的任何属性做动画,不仅仅只是view,而且在动画效果上,属性动画也不像view动画那样只局限与平移、缩放、旋转、透明度这四种,各种各样的自定义绚丽的效果都可以实现。

常用的动画类:ValueAnimator、ObjectAnimator、AnimatorSet,从类名可以看到,这分别是值动画、对象动画、动画集合。ObjectAnimator对象动画是继承于ValueAnimator动画的

一、ValueAnimator—值动画

单纯的ValueAnimator并不常用,因为只是单纯的对值进行操作而不赋予对象的属性上的话,意义并不大,用法很简单,将一个值在1秒内从0过渡到1,可以这么写:

ValueAnimator ani = ValueAnimator.ofFloat(0f,1f);
ani.setDuration(1000);
ani.start();


这样动画就可以运行了,只是看不出来,因为单纯的一个值的变化在屏幕上是一片空白,不过可以通过监听器来监控动画,添加监听有两种方法,addListener和addUpdateListener,分别对应不同的监听器。

addListener对应的是AnimatorListener监听器,可以使用如下代码添加:

ani.addListener(new AnimatorListener() {

@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
//动画开始的时候,此方法被触发
}

@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
//动画重复的时候,此方法被触发
}

@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
//动画结束的时候,此方法被触发
}

@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
//动画取消的时候,此方法被触发
}
});


但是一般情况下,我们并不需要监听这么多状态,所以官方提供了另外一种方式,如下:

ani.addListener(new AnimatorListenerAdapter() {

//          @Override
//          public void onAnimationEnd(Animator animation) {
//              // TODO Auto-generated method stub
//              super.onAnimationEnd(animation);
//              //当动画结束的时候,此方法被触发
//          }

});


AnimatorListenerAdapter是一个AnimatorListener的适配器类,它已经实现了AnimatorListener的所有接口,所以使用它的时候不必将每一个方法都实现,只需要在需要的地方重写就可以了,其他的都会是默认实现。

PS:此类监听器本人习惯称之为状态监听器(有开始、结束、重复、取消等状态)

系统还提供了另外一种监听方法:addUpdateListener,对应的监听器为AnimatorUpdateListener,使用方法如下:

ani.addUpdateListener(new AnimatorUpdateListener() {

@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
float f = (Float)animation.getAnimatedValue();
}
});


和上面的状态监听器不同的是,这个AnimatorUpdateListener监听器监听的是动画的实时更新,动画是由很多帧组成了,每播放一帧,onAnimationUpdate方法就会调用一次。

PS:此类监听器本人习惯称为进行时监听器,它监听的是动画的实时播放。

ValueAnimator常用的方法:

ValueAnimator ani = ValueAnimator.ofInt(0,100,200);//对一个int值进行动画过渡
ani.setDuration(3000);//设置动画播放时长,单位为毫秒
ani.setRepeatCount(ValueAnimator.INFINITE);//设置循环次数,-1时则是无限循环,ValueAnimator.INFINITE值就是-1
ani.setRepeatMode(ValueAnimator.REVERSE);//设置循环模式,有两种,RESTRAT重新播放,REVERSE倒序播放
ani.setStartDelay(1000);//延时播放
ani.setEvaluator(new PointEvaluator());//设置自定义估值器
//设置插值器
ani.setInterpolator(new TimeInterpolator() {

@Override
public float getInterpolation(float input) {
// TODO Auto-generated method stub
return 0;
}
});


二、ObjectAnimator对象动画

和ValueAnimator值动画相比,ObjectAnimator对象动画用的就比较多,它的功能相当强大,几乎可以对任何对象的任意属性做动画,用法也很简单,如下:

//从当前位置向左移动300距离再移回来
float currentTranslationX = tv.getTranslationX();
ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-300f,currentTranslationX);
ani.setDuration(2000);
ani.start();


上面的方法的第一个参数是目标对象,这里是一个textView,第二个参数是目标对象的目标属性,这里是translationX,后面是若干个属性值。translationX属性控制着控件的X坐标,将一个textView的translationX的属性做动画,X坐标不停的变换,那么整个控件就会产生了动画效果。

属性动画机制其实是不停的给对象(比如View)的某个属性赋值(比如translationX),每一次赋值一般都会触发视图重绘(如果没有那么不会产生动画效果,需要自己手动强制重绘),内部原理其实是:属性动画在改变对象的某个属性的时候(比如属性abc),需要对象提供方法setAbc,这样属性动画就会根据外部传进来的初始值和最终值进行多次调用setAbc方法来给相应的属性赋值,这样在一个时间段内,属性abc被多次改变,越来越接近最终值,如果有触发重绘的话,就会产生动画。

两个注意点:

1、 想要通过属性动画来使对象的某个属性abc进行改变,那么对象就要有setAbc方法,同时,外部传值的时候如果没有初始值,那么对象还需要提供getAbc方法用来获取初始值。

2、 想要有动画的效果,那么对象的属性在改变的时候需要造成UI的改变,否则看不到动画效果。

至于监听器方面,由于ObjectAnimator是继承于ValueAnimator,所以用法和ValueAnimator是一样的,不多说。

三、AnimatorSet 动画集合

真正的项目中的动画都是绚丽多彩的,仅仅靠一个动画很难实现,所以动画集合AnimatorSet也很常用,用法也很简单,如下:

//向左移动
ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-300f,currentTranslationX);
//透明度变换
ObjectAnimator ani1 = ObjectAnimator.ofFloat(tv, "alpha", 1f,0f,1f);
//旋转180度
ObjectAnimator ani2 = ObjectAnimator.ofFloat(tv, "rotation", 0f,180f);
AnimatorSet as = new AnimatorSet();
as.setDuration(2000);
as.play(ani).with(ani1).after(ani2);//先旋转之后,移动和透明度同时改变,with属于同时播放,after和before属于有序播放
as.start();


也可以这样:

ObjectAnimator ani = ObjectAnimator.ofFloat(tv, "translationX", currentTranslationX,-500f,currentTranslationX);
ObjectAnimator ani1 = ObjectAnimator.ofFloat(tv, "rotation", 0f,360f);
ObjectAnimator ani2 = ObjectAnimator.ofFloat(tv, "alpha", 1f,0f,1f);
AnimatorSet as = new AnimatorSet();
//有序进行
//      as.playSequentially(ani,ani1,ani2);
//同时进行
as.playTogether(ani,ani1,ani2);
as.setDuration(3000);
as.start();


方法很简单,AnimatorSet动画集合有两种方式,一个就是有序播放,还一个就是同时播放

四、使用xml方式来播放动画

和之前的view动画一样,属性动画也可以写在xml中,方便复用,代码也很简单,在res目录中创建animator文件夹,创建animator.xml文件,内容如下:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially">

<animator
android:duration="2000"
android:repeatCount="2"
android:valueFrom="100"
android:valueTo="300"
android:valueType="intType"/>
<objectAnimator
android:propertyName="translationX"
android:duration="3000"
android:valueFrom="-500"
android:valueTo="0"
android:valueType="floatType"></objectAnimator>
<set
android:ordering="together">
<objectAnimator
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="180"
android:valueType="floatType"
android:duration="2000"
/>
<set android:ordering="sequentially">
<objectAnimator
android:propertyName="alpha"
android:valueFrom="1"
android:valueTo="0"
android:valueType="floatType"
android:duration="1000"/>
<objectAnimator
android:duration="1000"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"/>
</set>
</set>

</set>


其中animator是ValueAnimator,objectAnimator对应的是ObjectAnimator,Set对应的是动画集合AnimatorSet,set动画集合有一个属性是ordering,两个值,sequentially代表的是有序,together代表同时,其他的代码很简单,就不多做解释了,注意一点,当改动的属性propertyName是颜色的时候,valueType就不需要指定了,系统会自动处理。

使用xml方式的动画可以复用,但是加载会稍微慢一些,而且需要提前知道初始值和结束值并且固定,代码中的动画无法复用,但是加载速度快,初始值和结束值不需要定死,实际使用可以按照需求来选择。

五、TypeEvaluater估值器和TimeInterpolator 时间插值器

1、TypeEvaluater估值器

作用是根据当前属性改变的百分比来计算改变之后的属性值

TypeEvaluater是一个接口,里面只有一个方法,如下:

public interface TypeEvaluator<T> {

/**
* This function returns the result of linearly interpolating the start and end values, with
* <code>fraction</code> representing the proportion between the start and end values. The
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
* and <code>t</code> is <code>fraction</code>.
*
* @param fraction   The fraction from the starting to the ending values
* @param startValue The start value.
* @param endValue   The end value.
* @return A linear interpolation between the start and end values, given the
*         <code>fraction</code> parameter.
*/
public T evaluate(float fraction, T startValue, T endValue);

}


这个方法有三个参数,第一个参数是浮点类型fraction,意味着当前属性改变的百分比,后面两个参数代表这初始对象和终止对象,通过一系列算法最后返回一个同样的对象,这个返回的对象就是当前瞬间的状态,拿系统提供的IntegerEvaluator为例

public class IntEvaluator implements TypeEvaluator<Integer> {

public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
int startInt = startValue;
return (int)(startInt + fraction * (endValue - startInt));
}
}


这里面的对象就是一个整形,传进去的三个参数中,结束值减去初始值就是整个的变化区间,乘以当前的属性改变百分比,就是当前变化的值,再加上初始值返回,即当前瞬间的值。

初始值和结束值肯定是外部传进去的,那么第一个参数fraction(属性改变的百分比)是从哪里得到的呢,其实是从TimeInterpolator时间插值器中得到的。

2、TimeInterpolator时间插值器

作用是根据时间变化百分比来计算当前属性改变的百分比

同样,先看看接口

public interface TimeInterpolator {

/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* @param input A value between 0 and 1.0 indicating our current point
*        in the animation where 0 represents the start and 1.0 represents
*        the end
* @return The interpolation value. This value can be more than 1.0 for
*         interpolators which overshoot their targets, or less than 0 for
*         interpolators that undershoot their targets.
*/
float getInterpolation(float input);
}


接口方法也很简单,只有一个方法,方法中的参数input是从系统中获得,这个参数的值随着动画的运行不断变化,并且是匀速变化,可以理解为“时间”,时间流逝是匀速的,这里面就是根据时间流逝百分比来计算属性变化的百分比,返回一个瞬时的浮点型,这个浮点型就是估值器中的fraction。

流程:动画开始–>插值器算法通过时间流逝得到属性变化百分比fraction–>估值器算法通过属性变化百分比得到当前属性值–>动画结束。

常用的系统提供的插值器有:

LinearInterpolator线性插值器(匀速变化)、AccelerateDecelerateIntepolator加速减速插值器、

AccelerateIntepolator加速插值器和DecelerateIntepolator减速插值器等,如果要自定义插值器就需要实现Interpolator或者TimeInterpolator。

常用的系统提供的估值器有:

FloatEvaluator浮点估值、IntEvaluator整形估值、ArgbEvaluator颜色估值器,如果自定义估值器的话就需要实现TypeEvaluator。

六、对任意属性做动画

前面说属性动画原理机制的时候提到过,要想有动画效果,需要两个注意点:

1、 想要通过属性动画来使对象的某个属性abc进行改变,那么对象就要有setAbc方法,同时,外部传值的时候如果没有初始值,那么对象还需要提供getAbc方法用来获取初始值。

2、 想要有动画的效果,那么对象的属性在改变的时候需要造成UI的改变,否则看不到动画效果。

那么如果想要给某个对象做动画,但是对象并没有提供相应的setAbc方法,或者相应的setAbc方法并没有达到想要的效果,比如TextView的setWidtht方法,并不是控制了View的宽度,而是最小和最大宽度,所以这种情况下,就需要自己想办法。

分析要想达到的效果是:宽度不停的变化从而产生动画。

有两种方法可以实现:

1、 对目标对象进行包装,定义set和get方法,在set方法内进行目标对象的属性变化并且更新UI

例如:

private class ViewHolder{
private TextView tv;
public ViewHolder(TextView tv){
this.tv = tv;
}
public void setWidth(int width){
tv.getLayoutParams().width = width;
tv.requestLayout();
}
public int getWidth(){
return tv.getLayoutParams().width;
}
public void setHeight(int height){
tv.getLayoutParams().height = height;
tv.requestLayout();
}
public int getHeight(){
return tv.getLayoutParams().height;
}
}


在外部调用的时候就需要如此写即可

public void startAnimation(){
ViewHolder viewHolder = new ViewHolder(tv);
ObjectAnimator ani = ObjectAnimator.ofInt(viewHolder, "width", 500).setDuration(3000);
ani.start();
}


2、 ValueAnimator类中可以添加进行时监听器addUpdateListener,监听每一帧的变化,并且每一帧都会调用,那么直接在方法内添加对对象属性的改变并且重新绘制即可,这样就会在每一帧的时候变化对象的属性,继而产生动画。如下:

public void startAnimation(){
//      ViewHolder viewHolder = new ViewHolder(tv);
//      ObjectAnimator ani = ObjectAnimator.ofInt(viewHolder, "width", 500).setDuration(3000);
//      ani.start();
ValueAnimator ani = ValueAnimator.ofFloat(0,1);
ani.addUpdateListener(new AnimatorUpdateListener() {

@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
float f = animation.getAnimatedFraction();
Log.v("Animator", "属性改变百分比是:"+f);
tv.getLayoutParams().height = (int)(f*500);
tv.requestLayout();
}
});
ani.setDuration(3000);
ani.start();
}


这个其实就是利用了监听器,进行时监听器监听的是动画的实时播放,所以在方法内做属性的改变,每一帧调用一次,整体就形成了动画…

PS:总感觉类似于“挂载”?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息