Android动画解析(二)

来源:互联网 发布:农村淘宝地址怎么改 编辑:程序博客网 时间:2024/06/08 16:43

一、插值器和估值器:

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;}
@Overridepublic 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
  • 动画过程,开启硬件加速


原创粉丝点击