Android动画解析(二)

一、插值器和估值器:

Interpolator(插值器)和TypeEvaluator(估值器)是实现非匀速动画的重要手段
1、Interpolator: 过渡值变化的规则( 控制动画的进度 )
	* AccelerateDecelerateInterpolator   在动画开始与介绍的地方速率改变比较慢,在中间的时候加速
	* AccelerateInterpolator                     在动画开始的地方速率改变比较慢,然后开始加速
	* AnticipateInterpolator                      开始的时候向后然后向前甩
	* AnticipateOvershootInterpolator     开始的时候向后然后向前甩一定值后返回最后的值
	* BounceInterpolator                          动画结束的时候弹起
	* CycleInterpolator                             动画循环播放特定的次数,速率改变沿着正弦曲线
	* DecelerateInterpolator                    在动画开始的地方快然后慢
	* LinearInterpolator                            以常量速率改变
	* OvershootInterpolator                      向前甩一定值后再回到原来位置

2、自定义插值器:
实现TimeInterpolator。TimeInterpolator:时间插值器,根据时间的百分比计算属性改变的百分比

   class MyInterploator implements TimeInterpolator{
        @Override
        public float getInterpolation(float input) {
            return 1-input;
        }
    }
在getInterpolation函数中,直接把input值返回,即以当前动画的进度做为动画的数值进度,这也就表示当前动画的数值进度与动画的时间进度一致
3、evaluator  估值器
将从加速器返回的数字进度转成对应的数字值。
通过在AnimatorUpdateListener监听器使用animation.getAnimatedValue()函数拿到Evaluator中返回的数字值。 
ofInt()对应的Evaluator类名叫IntEvaluator,而ofFloat()对应的Evaluator类名叫FloatEvaluator; 
public class IntEvaluator implements TypeEvaluator<Integer> {

    /**
     * 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; should be of type <code>int</code> or
     *                   <code>Integer</code>
     * @param endValue   The end value; should be of type <code>int</code> or <code>Integer</code>
     * @return A linear interpolation between the start and end values, given the
     *         <code>fraction</code> parameter.
     */
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
        int startInt = startValue;
        return (int)(startInt + fraction * (endValue - startInt));
    }
}
fraction就是加速器中的返回值,表示当前动画的数值进度,百分制的小数表示。 
startValue和endValue分别对应ofInt(int start,int end)中的start和end的数值;
4、自定义估值器:
	public class MyEvaluator implements TypeEvaluator<Integer> {  
        @Override  
        public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
            int startInt = startValue;
            return (int)(200+startInt + fraction * (endValue - startInt));
        }  
	}
5、ArgbEvalutor  颜色过度的估值器
 ValueAnimator animator = ValueAnimator.ofInt(0xffffff00, 0xff0000ff);
	animator.setEvaluator(new ArgbEvaluator());
	animator.setDuration(3000);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
        @Override
        public void onAnimationUpdate (ValueAnimator animation){
            int curValue = (int) animation.getAnimatedValue();
            tv.setBackgroundColor(curValue);
         }
    });
    animator.start();
三、ObjectAnimator 
继承于ValueAnimator
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) 
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) 
public static ObjectAnimator ofObject(Object target, String propertyName,TypeEvaluator evaluator, Object... values)
  • 第一个参数用于指定这个动画要操作的是哪个控件
  • 第二个参数用于指定这个动画要操作这个控件的哪个属性
  • 第三个参数是可变长参数,这个就跟ValueAnimator中的可变长参数的意义一样了,就是指这个属性值是从哪变到哪。
1、属性动画的原理:
object必须提供该属性的set方法,多次调用set方法,每次传给set的值不一样。
如果没初始值,还需要get方法,因为系统要获取属性的初始值。
例如:
ObjectAnimator animator = ObjectAnimator.ofFloat(tv,"rotation",0,180,0);
ObjectAnimator在做动画时就会到指定控件(TextView)中去找对应的setRotation()方法来改变控件中对应的值。
public void setRotation(float rotation) {
    if (rotation != getRotation()) {
        // Double-invalidation is necessary to capture view's old and new areas
        invalidateViewProperty(true, false);
        mRenderNode.setRotation(rotation);
        invalidateViewProperty(false, true);

        invalidateParentIfNeededAndWasQuickRejected();
        notifySubtreeAccessibilityStateChangedIfNeeded();
    }
}
调用的内部进行属性调整,通过invalidate进行重绘。

View关于动画的set方法如下:
//透明度:alpha 
public void setAlpha(float alpha) 

//旋转度数:rotation、rotationX、rotationY 
public void setRotation(float rotation) 
public void setRotationX(float rotationX) 
public void setRotationY(float rotationY) 

//平移:translationX、translationY 
public void setTranslationX(float translationX)   
public void setTranslationY(float translationY) 

//缩放:scaleX、scaleY 
public void setScaleX(float scaleX) 
public void setScaleY(float scaleY)

2、自定义控件

public class MyPointView extends View {
    private Point mPoint = new Point(100);

    public MyPointView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (mPoint != null){
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setColor(Color.RED);
            paint.setStyle(Paint.Style.FILL);
            canvas.drawCircle(300,300,mPoint.getRadius(),paint);
        }
        super.onDraw(canvas);
    }

    void setPointRadius(int radius){
        mPoint.setRadius(radius);
        invalidate();
    }
     //初始值
     public int getPointRadius(){
          return 20;
     }

} 
private void doPointViewAnimation(){
    ObjectAnimator animator = ObjectAnimator.ofInt(mPointView, "pointRadius", 0, 300, 100);
    animator.setDuration(2000);
    animator.start();
} 
四、PropertyValuesHolder
1、保存动画过程所需操作的属性和对应的值。
ofFloat内部实现是将传入的参数封装为PropertyValuesHolder 。
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    anim.setFloatValues(values);
    return anim;
}
@Override
public void setFloatValues(float... values) {
    if (mValues == null || mValues.length == 0) {
        // No values yet - this animator is being constructed piecemeal. Init the values with
        // whatever the current propertyName is
        if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}
public void setValues(PropertyValuesHolder... values) {
    int numValues = values.length;
    mValues = values;
    mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
    for (int i = 0; i < numValues; ++i) {
        PropertyValuesHolder valuesHolder = values[i];
        mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
    }
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}
2、构造PropertyValuesHolder:
public static PropertyValuesHolder ofFloat(String propertyName, float... values)
public static PropertyValuesHolder ofInt(String propertyName, int... values)
public static PropertyValuesHolder ofObject(String propertyName, TypeEvaluator evaluator, Object... values)
public static PropertyValuesHolder ofKeyframe(String propertyName, Keyframe... values)
propertyName: 需要操作的参数名
values : 属性对应可变参数
evaluator:估值器
3、返回ObjectAnimator对象
ObjectAnimator.ofPropertyValuesHolder(Object target,PropertyValuesHolder... values)
target: 需要执行动画的控件
values : 可以是多个PropertyValuesHolder,每一个PropertyValuesHolder对一个属性做动画,多个实例可对控件多个属性动画。
PropertyValuesHolder rotationHolder = PropertyValuesHolder.ofFloat("Rotation", 60f, -60f, 40f, -40f, -20f, 20f, 10f, -10f, 0f);
PropertyValuesHolder colorHolder = PropertyValuesHolder.ofInt("BackgroundColor", 0xffffffff, 0xffff00ff, 0xffffff00, 0xffffffff);
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(mTextView, rotationHolder, colorHolder);
animator.setDuration(3000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
类似于动画集合
(3)ofObject:
例:改变TextView textSize属性
注意:改变属性的类型要匹配,控件有set方法(可自己扩展)
private void startAnimator() {
    PropertyValuesHolder charHolder = PropertyValuesHolder.ofObject("textSize",new FloatEvaluator(),10f,20f);
    ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(myText, charHolder);
    animator.setDuration(5000);
    animator.setInterpolator(new AccelerateInterpolator());
    animator.start();
}

class FloatEvaluator implements TypeEvaluator<Float>{
    @Override
    public Float evaluate(float fraction, Float startValue, Float endValue) {
        return fraction * (endValue -startValue);
    }
}
4、Keyframe: 关键帧,为了控制动画速率
/**
 * ofFloat 
 */
public static Keyframe ofFloat(float fraction)
public static Keyframe ofFloat(float fraction, float value)
/**
 * ofInt 
 */
public static Keyframe ofInt(float fraction)
public static Keyframe ofInt(float fraction, int value) 
fraction:当前进度
value:当前位置
例:

Keyframe frame0 = Keyframe.ofFloat(0f, 0);
Keyframe frame1 = Keyframe.ofFloat(0.1f, -20f);
Keyframe frame2 = Keyframe.ofFloat(1, 0);
PropertyValuesHolder frameHolder = PropertyValuesHolder.ofKeyframe("rotation",frame0,frame1,frame2);
Animator animator = ObjectAnimator.ofPropertyValuesHolder(mImage,frameHolder); 
animator.setDuration(1000); 
animator.start();
2)常用函数:
/**
 * 设置fraction参数,即Keyframe所对应的进度 
 */
public void setFraction(float fraction)
/**
 * 设置当前Keyframe所对应的值 
 */
public void setValue(Object value)
/**
 * 前一个Keyframe到当前Keyframe期间所对应的插值器 
 */
public void setInterpolator(TimeInterpolator interpolator) 
注:
  • 如果去掉第0帧,将以第一个关键帧为起始位置
  • 如果去掉结束帧,将以最后一个关键帧为结束位置
  • 使用Keyframe来构建动画,至少要有两个或两个以上帧
五、AnimatorSet 
1、 playSequentially表示所有动画依次播放,playTogether表示所有动画一起开始。
public void playSequentially(Animator... items);
public void playSequentially(List<Animator> items);
public void playTogether(Animator... items);
public void playTogether(Collection<Animator> items);
private void doPlaySequentiallyAnimator(){
    ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor",  0xffff00ff, 0xffffff00, 0xffff00ff);
    ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 300, 0);
    ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);

    AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playSequentially(tv1BgAnimator,tv1TranslateY,tv2TranslateY);
    animatorSet.setDuration(1000);
    animatorSet.start();
}
注:
  • 如果去掉第0帧,将以第一个关键帧为起始位置
  • 如果去掉结束帧,将以最后一个关键帧为结束位置
  • 使用Keyframe来构建动画,至少要有两个或两个以上帧

2、AnimatorSet.Builder
//表示要播放哪个动画
public Builder play(Animator anim)
//和前面动画一起执行
public Builder with(Animator anim)
//执行前面的动画后才执行该动画
public Builder before(Animator anim)
//执行先执行这个动画再执行前面动画
public Builder after(Animator anim)
//延迟n毫秒之后执行动画
public Builder after(long delay)
串行方式执行:
ObjectAnimator tv1BgAnimator = ObjectAnimator.ofInt(mTv1, "BackgroundColor",  0xffff00ff, 0xffffff00, 0xffff00ff);
ObjectAnimator tv1TranslateY = ObjectAnimator.ofFloat(mTv1, "translationY", 0, 400, 0);
ObjectAnimator tv2TranslateY = ObjectAnimator.ofFloat(mTv2, "translationY", 0, 400, 0);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(tv1TranslateY).with(tv2TranslateY).after(tv1BgAnimator);
animatorSet.setDuration(2000);
animatorSet.start();
3、函数:
//设置单次动画时长
public AnimatorSet setDuration(long duration);
//设置加速器
public void setInterpolator(TimeInterpolator interpolator)
//设置ObjectAnimator动画目标控件
public void setTarget(Object target)
//设置延时开始动画时长(只是延长AnimatorSet的开始时间)
public void setStartDelay(long startDelay)
四、对任意属性做动画(自定义动画):
1、属性动画的原理:
(1)object必须提供该属性的set方法,多次调用set方法,每次传给set的值不一样。
如果没初始值,还需要get方法,因为系统要获取属性的初始值。

(2)对于属性的改变必须通过某种ui的变化体现出来(不然动画无效)
解决方式:
1、给你的对象加上set和get方法(或者用一个类包装原始类,间接提供get和set方法。)
public class Animator {
    private Button button;
    private void performAnimate(){
        //按钮点击后执行的动画
        ViewWrapper viewWrapper = new ViewWrapper(button);
        ObjectAnimator.ofInt(viewWrapper,"width",500).setDuration(5000).start();
    }

    private static class ViewWrapper{
        private View mtagerView;

        public ViewWrapper(View target){
            mtagerView = target;
        }

        public int getWidth(){
            return mtagerView.getLayoutParams().width;
        }
        public void setWidth(int width){
            mtagerView.getLayoutParams().width = width;
            mtagerView.requestLayout();
        }
    }
}
2、采用ValueAnimator,监听动画过程,自己实现属性的改变
public void performAnimate(final int start, final int end, final View targetView){
    final ValueAnimator valueAnimator =ValueAnimator.ofInt(1,100);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        //整形估值器,估值时使用
        private IntEvaluator intEvaluator = new IntEvaluator();
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            //获取动画的进度
            int currValue=(Integer) animation.getAnimatedValue();
            //当前进度占整个动画的比例
            float fraction= animation.getAnimatedFraction();
            //通过比例计算出宽度,然后在设给Button
            targetView.getLayoutParams().width=intEvaluator.evaluate(fraction,start,end);
            targetView.requestLayout();
        }
    });
    valueAnimator.setDuration(5000).start(); //5000ms内1-100
}
3、使用动画注意事项
  • OOM:数量较多并图片较大
  • 内存泄露:无限循环动画,需要在Activity中退出时停止
  • 兼容性问题
  • View动画完成,View无法隐藏。需要调用clearAnimation清除View动画
  • 尽量使用dp
  • 动画过程,开启硬件加速


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值