【HenCoder Android 开发进阶】自定义 View 1-7:属性动画(进阶篇)

这期是 HenCoder 自定义绘制的第 1-7 期:属性动画(进阶篇)java

属性动画的上手篇在这里:
HenCoder Android 开发进阶:自定义 View 1-6 属性动画(上手篇)git

若是你没据说过 HenCoder,能够先看看这个:
HenCoder:给高级 Android 工程师的进阶手册github

简介

上期的内容,对于大多数简单的属性动画场景已经够用了。这期的内容主要针对两个方面:微信

  1. 针对特殊类型的属性来作属性动画;
  2. 针对复杂的属性关系来作属性动画。

TypeEvaluator

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

关于 TypeEvaluator 是什么和怎么用,先看一下下面的视频吧:post

若是你是手机打开的,能够点这里去 B 站看。性能

ArgbEvaluator

如视频中的例子,TypeEvaluator 最经典的用法是使用 ArgbEvaluator 来作颜色渐变的动画。动画

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xffff0000, 0xff00ff00);
animator.setEvaluator(new ArgbEvaluator());
animator.start();复制代码

另外,在 Android 5.0 (API 21) 加入了新的方法 ofArgb(),因此若是你的 minSdk 大于或者等于 21(哈哈哈哈哈哈哈哈),你能够直接用下面这种方式:lua

ObjectAnimator animator = ObjectAnimator.ofArgb(view, "color", 0xffff0000, 0xff00ff00);
animator.start();复制代码

自定义 Evaluator

若是你对 ArgbEvaluator 的效果不满意,或者你因为别的什么缘由但愿写一个自定义的 TypeEvaluator,你能够这样写:spa

// 自定义 HslEvaluator
private class HsvEvaluator implements TypeEvaluator<Integer> {
   float[] startHsv = new float[3];
   float[] endHsv = new float[3];
   float[] outHsv = new float[3];

   @Override
   public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
       // 把 ARGB 转换成 HSV
       Color.colorToHSV(startValue, startHsv);
       Color.colorToHSV(endValue, endHsv);

       // 计算当前动画完成度(fraction)所对应的颜色值
       if (endHsv[0] - startHsv[0] > 180) {
           endHsv[0] -= 360;
       } else if (endHsv[0] - startHsv[0] < -180) {
           endHsv[0] += 360;
       }
       outHsv[0] = startHsv[0] + (endHsv[0] - startHsv[0]) * fraction;
       if (outHsv[0] > 360) {
           outHsv[0] -= 360;
       } else if (outHsv[0] < 0) {
           outHsv[0] += 360;
       }
       outHsv[1] = startHsv[1] + (endHsv[1] - startHsv[1]) * fraction;
       outHsv[2] = startHsv[2] + (endHsv[2] - startHsv[2]) * fraction;

       // 计算当前动画完成度(fraction)所对应的透明度
       int alpha = startValue >> 24 + (int) ((endValue >> 24 - startValue >> 24) * fraction);

       // 把 HSV 转换回 ARGB 返回
       return Color.HSVToColor(alpha, outHsv);
   }
}

ObjectAnimator animator = ObjectAnimator.ofInt(view, "color", 0xff00ff00);
// 使用自定义的 HslEvaluator
animator.setEvaluator(new HsvEvaluator());
animator.start();复制代码

ofObject()

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

  1. 为目标属性写一个自定义的 TypeEvaluator
  2. 使用 ofObject() 来建立 Animator,并把自定义的 TypeEvaluator 做为参数填入
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();复制代码

另外在 API 21 中,已经自带了 PointFEvaluator 这个类,因此若是你的 minSdk 大于或者等于 21(哈哈哈哈哈哈哈哈),上面这个类你就不用写了,直接用就好了。

ofMultiInt() ofMultiFloat()

在 API 引入的新的方法还有 ofMultiInt()ofMultiFloat() 等,用法也很简单,不过实用性就低了一些。你有兴趣的话能够去作一下了解,这里不在多作介绍。

以上这些就是对 TypeEvaluator 的介绍。它的做用是让你能够对一样的属性有不一样的解析方式,对原本没法解析的属性也能够打造出你须要的解析方式。有了 TypeEvaluator,你的属性动画就有了更大的灵活性,从而有了无限的可能。

TypeEvaluator 是本期的第一部份内容:针对特殊的属性来作属性动画,它可让你「作到原本作不到的动画」。接下来是本期的第二部份内容:针对复杂的属性关系来作动画,它可让你「能作到的动画作起来更简单」。

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

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

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

而对于 ObjectAnimator,是不能这么用的。不过你可使用 PropertyValuesHolder 来同时在一个动画中改变多个属性。

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();复制代码

PropertyValuesHolder 的意思从名字能够看出来,它是一个属性值的批量存放地。因此你若是有多个属性须要修改,能够把它们放在不一样的 PropertyValuesHolder 中,而后使用 ofPropertyValuesHolder() 统一放进 Animator。这样你就不用为每一个属性单首创建一个 Animator 分别执行了。

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();复制代码

使用 playSequentially(),就可让两个动画依次播放,而不用为它们设置监听器来手动为他们监管协做。

AnimatorSet 还能够这么用:

// 两个动画同时执行
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();复制代码

有了 AnimatorSet ,你就能够对多个 Animator 进行统一规划和管理,让它们按照要求的顺序来工做。它的使用比较简单,具体的用法我写在讲义里,你能够看一下。

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

除了合并多个属性和调配多个动画,你还能够在 PropertyValuesHolder 的基础上更进一步,经过设置 Keyframe (关键帧),把同一个动画属性拆分红多个阶段。例如,你可让一个进度增长到 100% 后再「反弹」回来。

// 在 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();复制代码

第二部分,「关于复杂的属性关系来作动画」,就这么三种:

  1. 使用 PropertyValuesHolder 来对多个属性同时作动画;
  2. 使用 AnimatorSet 来同时管理调配多个动画;
  3. PropertyValuesHolder 的进阶使用:使用 PropertyValuesHolder.ofKeyframe() 来把一个属性拆分红多段,执行更加精细的属性动画。

ValueAnimator 最基本的轮子

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

除了 ViewPropertyAnimator 和 ObjectAnimator,还有第三个选择是 ValueAnimator。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。

练习项目

为了不转头就忘,强烈建议你趁热打铁,作一下这个练习项目:HenCoderPracticeDraw7

下期预告

下期是绘制部分的最后一期:硬件加速相关。内容会比较少,也会比较简单。

绘制部分终于要完了哎,期待吗?

以为赞?

若是你看完以为有收获,把文章转发到你的微博、微信群、朋友圈、公众号,让其余须要的人也看到吧。

相关文章
相关标签/搜索