Android 自定义View:属性动画(六)

ViewPropertyAnimator

使用方式:View.animate() 后跟 translationX() 等方法,动画会自动执行canvas

view.animate().translationX(500);
复制代码

ObjectAnimator

使用方式:bash

  • 若是是自定义控件,须要添加 setter / getter 方法,并在setter方法的最后调用invalidate()方法,刷新绘制;
  • 用 ObjectAnimator.ofXXX() 建立 ObjectAnimator 对象;
  • 用 start() 方法执行动画。
public class SportsView extends View {

    float progress = 0;

    ......

    // 建立 getter 方法
    public float getProgress() {
        return progress;
    }

    // 建立 setter 方法
    public void setProgress(float progress) {
        this.progress = progress;
        invalidate();
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        ......

        canvas.drawArc(arcRectF, 135, progress * 2.7f, false, paint);

        ......
    }
}

......

// 建立 ObjectAnimator 对象
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "progress", 0, 65);
// 执行动画
animator.start();
复制代码

设置监听器

给动画设置监听器,能够在关键时刻获得反馈,从而及时作出合适的操做,例如在动画的属性更新时同步更新其余数据,或者在动画结束后回收资源等。ide

设置监听器的方法, ViewPropertyAnimatorObjectAnimator 略微不同: ViewPropertyAnimator用的是 setListener()setUpdateListener()方法,能够设置一个监听器,要移除监听器时经过 set[Update]Listener(null) 填 null 值来移除;而 ObjectAnimator则是用 addListener()addUpdateListener() 来添加一个或多个监听器,移除监听器则是经过 remove[Update]Listener() 来指定移除对象。post

另外,因为 ObjectAnimator 支持使用 pause()方法暂停,因此它还多了一个 addPauseListener() / removePauseListener() 的支持; ViewPropertyAnimator 则独有 withStartAction()withEndAction() 方法,能够设置一次性的动画开始或结束的监听,在动画执行结束后就自动丢弃,就算以后再重用 ViewPropertyAnimator 来作别的动画,用它们设置的回调也不会再被调用。而 set/addListener() 所设置的 AnimatorListener 是持续有效的,当动画重复执行时,回调总会被调用。性能

须要说明一下的是,就算动画被取消,onAnimationEnd() 也会被调用。因此当动画被取消时,若是设置了 AnimatorListener,那么 onAnimationCancel()onAnimationEnd() 都会被调用。onAnimationCancel() 会先于 onAnimationEnd() 被调用。动画

withEndAction() 设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和 AnimatorListener.onAnimationEnd() 的行为是不一致的。ui

TypeEvaluator

关于 ObjectAnimator,上面讲到能够用 ofInt() 来作整数的属性动画和用ofFloat() 来作小数的属性动画。这两种属性类型是属性动画最经常使用的两种,不过在实际的开发中,能够作属相动画的类型仍是有其余的一些类型。当须要对其余类型来作属性动画的时候,就须要用到 TypeEvaluator 了。this

自定义 Evaluator

借助于 TypeEvaluator,属性动画就能够经过 ofObject()来对不限定类型的属性作动画了。方式很简单:lua

private class PointFEvaluator implements TypeEvaluator<PointF> {
   PointF newPoint = new PointF();

   @Override
   public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
       float x = startValue.x + (fraction * (endValue.x - startValue.x));
       float y = startValue.y + (fraction * (endValue.y - startValue.y));

       newPoint.set(x, y);

       return newPoint;
   }
}

ObjectAnimator animator = ObjectAnimator.ofObject(view, "position",
        new PointFEvaluator(), new PointF(0, 0), new PointF(1, 1));
animator.start();
复制代码

PropertyValuesHolder 同一个动画中改变多个属性

不少时候,你在同一个动画中会须要改变多个属性,例如在改变透明度的同时改变尺寸。若是使用 ViewPropertyAnimator,你能够直接用连写的方式来在一个动画中同时改变多个属性:spa

view.animate()
        .scaleX(1)
        .scaleY(1)
        .alpha(1);
复制代码

ObjectAnimator 同一个动画中改变多个属性

使用 PropertyValuesHolder 来同时在一个ObjectAnimator动画中改变多个属性。

PropertyValuesHolder holder1 = PropertyValuesHolder.ofFloat("scaleX", 1);
PropertyValuesHolder holder2 = PropertyValuesHolder.ofFloat("scaleY", 1);
PropertyValuesHolder holder3 = PropertyValuesHolder.ofFloat("alpha", 1);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder1, holder2, holder3)
animator.start();
复制代码

AnimatorSet 多个动画配合执行

有的时候,你不止须要在一个动画中改变多个属性,还会须要多个动画配合工做,好比,在内容的大小从 0 放大到 100% 大小后开始移动。这种状况使用 PropertyValuesHolder 是不行的,由于这些属性若是放在同一个动画中,须要共享动画的开始时间、结束时间、Interpolator 等等一系列的设定,这样就不能有前后次序地执行动画了。

这就须要用到 AnimatorSet 了。

ObjectAnimator animator1 = ObjectAnimator.ofFloat(...);
animator1.setInterpolator(new LinearInterpolator());
ObjectAnimator animator2 = ObjectAnimator.ofInt(...);
animator2.setInterpolator(new DecelerateInterpolator());

AnimatorSet animatorSet = new AnimatorSet();
// 两个动画依次执行
animatorSet.playSequentially(animator1, animator2);
animatorSet.start();


// 两个动画同时执行
animatorSet.playTogether(animator1, animator2);
animatorSet.start();


// 使用 AnimatorSet.play(animatorA).with/before/after(animatorB)
// 的方式来精确配置各个 Animator 之间的关系
animatorSet.play(animator1).with(animator2);
animatorSet.play(animator1).before(animator2);
animatorSet.play(animator1).after(animator2);
animatorSet.start();
复制代码

PropertyValuesHolders.ofKeyframe() 把同一个属性拆分

// 在 0% 处开始
Keyframe keyframe1 = Keyframe.ofFloat(0, 0);
// 时间通过 50% 的时候,动画完成度 100%
Keyframe keyframe2 = Keyframe.ofFloat(0.5f, 100);
// 时间见过 100% 的时候,动画完成度倒退到 80%,即反弹 20%
Keyframe keyframe3 = Keyframe.ofFloat(1, 80);

PropertyValuesHolder holder = PropertyValuesHolder.ofKeyframe("progress", keyframe1, keyframe2, keyframe3);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, holder);
animator.start();
复制代码

ValueAnimator 最基本的轮子

额外简单说一下 ValuesAnimator。不少时候,你用不到它,只是在你使用一些第三方库的控件,而你想要作动画的属性却没有 setter / getter 方法的时候,会须要用到它。

ValueAnimator 并不经常使用,由于它的功能太基础了。ValueAnimator 是 ObjectAnimator 的父类,实际上,ValueAnimator 就是一个不能指定目标对象版本的 ObjectAnimator。

ObjectAnimator 是自动调用目标对象的 setter 方法来更新目标属性的值,以及不少的时候还会以此来改变目标对象的 UI,而 ValueAnimator 只是经过渐变的方式来改变一个独立的数据,这个数据不是属于某个对象的,至于在数据更新后要作什么事,全都由你来定,你能够依然是去调用某个对象的 setter 方法(别这么为难本身),也能够作其余的事,无论要作什么,都是要你本身来写的,ValueAnimator 不会帮你作。

好比有的时候,你要给一个第三方控件作动画,你须要更新的那个属性没有 setter 方法,只能直接修改,这样的话 ObjectAnimator 就不灵了啊。怎么办?这个时候你就能够用 ValueAnimator,在它的 onUpdate() 里面更新这个属性的值,而且手动调用 invalidate()

因此,ViewPropertyAnimator、ObjectAnimator、ValueAnimator 这三种 Animator,它们实际上是一种递进的关系:从左到右依次变得更加难用,也更加灵活。但我要说明一下,它们的性能是同样的,由于 ViewPropertyAnimator 和 ObjectAnimator 的内部实现其实都是 ValueAnimator,ObjectAnimator 更是原本就是 ValueAnimator 的子类,它们三个的性能并无差异。它们的差异只是使用的便捷性以及功能的灵活性。因此在实际使用时候的选择,只要遵循一个原则就行:尽可能用简单的。

能用 View.animate() 实现就不用 ObjectAnimator,能用 ObjectAnimator 就不用 ValueAnimator。

目录结构

参考

相关文章
相关标签/搜索