目录
3、采用ValueAnimator,监听动画过程,自己实现属性的改变
属性动画是API 11新加入的特性,属性动画可以对任何对象做动画,甚至可以没有对象。
属性动画的原理:属性动画要求动画作用的对象提供该属性的get和set方法,属性动画根据外界传递的该属性的初始值和最终值,以动画的效果多次去调用set方法,每次传递给set方法的值都不一样,确切来说是随着时间的推移,所传递的值越来越接近最终的值。总结一下,我们对object的属性abc做动画,如果想要动画生效,要同时满足两个条件:
(1)、object必须要提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法,因为系统要去取adc属性的初始值(如果这条不满足,程序直接Crash)。
(2)、object的setAbc对属性abc所做的改变必须能够通过某种方法反应出来,比如会带来UI的改变之类的(如果这条不满足,动画无效果,但不会Crash)
--Android开发艺术探索
一、对任意属性做动画
从前面的满足属性动画所需条件可知,所针对的属性必须有get、set方法。那如果这个属性没有set和get方法呢?
遇到这种情况有三种解决方案:
(1)、给这个对象加上get和set方法,如果有权限的话;
(2)、用一个类来包装原始对象,间接为其提供get和set方法;
(3)、采用ValueAnimator,监听动画过程,自己实现属性的改变。
下面对这三种解决方案进行介绍:
1、给这个对象加上get和set方法,如果有权限的话
比如我们直接使用LinearLayout,并给它添加个背景,我们知道LinearLayout继承ViewGroup,ViewGroup继承View,ViewGroup和View都是没有setWidth方法的,我们也没有权限去修改LinearLayout的源码实现来给它加上setWidth方法,那这个时候我们如果想要使用这种解决方案的话,我们可以自定义LinearLayout,继承LinearLayout,给它添加个setWidth方法。如下,我们在setWidth方法中设置此控件的布局参数的宽为我们传入的值,然后调用requestLayout重绘。
package com.example.viewanimation;
import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.LinearLayout;
/**
* 自定义LinearLayout,添加SetWidth方法.
*/
public class AnLinearLay extends LinearLayout {
public AnLinearLay(Context context) {
super(context);
}
public AnLinearLay(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public AnLinearLay(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setWidth(int width){
Log.d("AnLinearLay", "setWidth() enter!");
getLayoutParams().width = width;
requestLayout();
}
}
通过调用ObjectAnimator.ofInt让目标LinearLayout在5000ms内宽度增加到500px。
mLin = findViewById(R.id.lin);
ObjectAnimator.ofInt(mLin,"width",500).setDuration(5000).start();
2、用一个类来包装原始对象,间接为其提供get和set方法
还是用LinearLayout的例子,我们前面说过,属性动画可以对任意对象做动画,既然LinearLayout不能直接作用于动画,那我们就用一个类来包装它,在这个类中添加getWidth和setWidth方法,这个时候我们直接属性动画作用这个类的对象,在这个类中操作LinearLayout的宽度。如下,其中target使我们传入的mLin。
public void performAnimate(View taget){
ViewWrapper viewWrapper = new ViewWrapper(taget);
ObjectAnimator.ofInt(viewWrapper,"width",500).setDuration(5000).start();
}
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();
}
}
3、采用ValueAnimator,监听动画过程,自己实现属性的改变
ValueAnimator本身不作用于任何对象,直接使用它是没有任何动画效果的。它可以对一个值多动画,然后我们可以监听器动画过程,在动画过程中修改我们的对象的属性值,这样也就相当于我们的动画做了动画。
实现方法如下,动画的每一帧都会回调onAnimationUpdate方法。每回调一次设置一次宽度。
public void performAnimate(final View target, final int start, final int end){
ValueAnimator valueAnimator = ValueAnimator.ofInt(1,100);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
//针对整型的估值器,后面需要使用估值器来计算宽度
private IntEvaluator mEvaluator = new IntEvaluator();
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//获得当前动画的进度值,整型,1-100之间
int currentValue = (int) animation.getAnimatedValue();
Log.d(TAG,"current value : " + currentValue);
//获得当前进度占整个动画过程的比例,浮点型,0-1之间
float fraction = animation.getAnimatedFraction();
target.getLayoutParams().width = mEvaluator.evaluate(fraction,start, end);
target.requestLayout();
}
});
valueAnimator.setDuration(5000).start();
}
mBt1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
performAnimate(mLin, mLin.getWidth(), 500);
}
});
二、背景色动画
给一个view的背景色做动画,如下:
//设置button的背景颜色从0xFFFF8080渐变到0xFF8080FF
ValueAnimator colorAnim = ObjectAnimator.ofInt(button,"backgroundColor",0xFFFF8080,0xFF8080FF);
colorAnim.setDuration(3000);
//设置针对color属性的估值器
colorAnim.setEvaluator(new ArgbEvaluator());
//设置重复次数为无限次
colorAnim.setRepeatCount(ValueAnimator.INFINITE);
//设置重复模式为反转
colorAnim.setRepeatMode(ValueAnimator.REVERSE);
colorAnim.start();
三、制作动画集合
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
ObjectAnimator.ofFloat(button,"rotationX",0,360),
ObjectAnimator.ofFloat(button,"rotationY",0,180),
ObjectAnimator.ofFloat(button,"rotation",0,-90),
ObjectAnimator.ofFloat(button,"translationX",0,90),
ObjectAnimator.ofFloat(button,"translationY",0,90),
ObjectAnimator.ofFloat(button,"scaleX",1,1.5f),
ObjectAnimator.ofFloat(button,"scaleY",1,0.5f),
ObjectAnimator.ofFloat(button,"alpha",1,0.25f,1)
);
animatorSet.setDuration(5 * 1000).start();
四、使用XML定义属性动画
xml文件路径:res/animator/
比如在创建文件res/animator/property_animator.xml如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:propertyName="x"
android:duration="3000"
android:valueTo="200dp"
android:valueType="floatType"/>
<objectAnimator
android:propertyName="y"
android:duration="3000"
android:valueTo="300dp"
android:valueType="floatType"/>
</set>
在代码中加载并开启动画
AnimatorSet animatorSet = (AnimatorSet) AnimatorInflater.loadAnimator(getApplication(),R.animator.property_animator);
animatorSet.setTarget(button);
animatorSet.start();
五、注意事项
如果使用属性动画中的一种无限循环的动画,那么在Activity退出时需要及时停止,避免内存泄漏。
colorAnim.end();
colorAnim.cancel();
这两个方法最后都会调用endAnimation()来结束动画,区别就是end方法执行后,动画会定格到动画结束的效果;cancel执行后,动画会定格到此时动画执行到的效果。