推荐一本书—《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:总感觉类似于“挂载”?
1636

被折叠的 条评论
为什么被折叠?



