Android自定义View(七) -- 属性动画(下)

前面学习的内容:
Android自定义View(一) -- 初识
Android自定义View(二) -- Paint详解
Android自定义View(三) -- drawText()
Android自定义View(四) -- Canvas
Android自定义View(五) -- 绘制顺序
Android自定义View(六) -- 属性动画(上)
今天继续学习Android自定义View第七篇内容 属性动画(下)
因为无法录制GIF,所以本篇内容基本为原博


本文计划根据HenCoder系列文章进行学习,所以代码风格及博文素材可能会摘自其中。


简介

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

针对特殊类型的属性来做属性动画;
针对复杂的属性关系来做属性动画。

TypeEvaluator

关于 ObjectAnimator,上期讲到可以用 ofInt() 来做整数的属性动画和用 ofFloat() 来做小数的属性动画。这两种属性类型是属性动画最常用的两种,不过在实际的开发中,可以做属性动画的类型还是有其他的一些类型。当需要对其他类型来做属性动画的时候,就需要用到 TypeEvaluator 了。

关于 TypeEvaluator 是什么和怎么用,先看一下下面的视频吧:(简书不支持插入视频)
https://www.bilibili.com/video/av14367465/?zw

ArgbEvaluator

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

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

image

另外,在 Android 5.0 (API 21) 加入了新的方法 ofArgb(),所以如果你的 minSdk 大于或者等于 21(哈哈哈哈哈哈哈哈),你可以直接用下面这种方式:

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

自定义 Evaluator

如果你对 ArgbEvaluator 的效果不满意,或者你由于别的什么原因希望写一个自定义的 TypeEvaluator,你可以这样写:

public class PracticeEvaluator implements TypeEvaluator<Integer> {

   private float[] startHsv = new float[3];
   private float[] endtHsv = new float[3];
   private float[] outHSV = new float[3];


    @Override
    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {

        //ARGB转换成HSV
        Color.colorToHSV(startValue,startHsv);
        Color.colorToHSV(endValue,endtHsv);

        outHSV[0] = startHsv[0] + (endtHsv[0] - startHsv[0]) * fraction;
        outHSV[1] = startHsv[1] + (endtHsv[1] - startHsv[1]) * fraction;
        outHSV[2] = startHsv[2] + (endtHsv[2] - startHsv[2]) * fraction;

        int alpha = 1;

        return Color.HSVToColor(alpha,outHSV);
    }
}
image

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();  

image

另外在 API 21 中,已经自带了 PointFEvaluator 这个类,所以如果你的 minSdk 大于或者等于 21(哈哈哈哈哈哈哈哈),上面这个类你就不用写了,直接用就行了。

image
image

ofMultiInt() ofMultiFloat()

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

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

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

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

很多时候,你在同一个动画中会需要改变多个属性,例如在改变透明度的同时改变尺寸。如果使用 ViewPropertyAnimator,你可以直接用连写的方式来在一个动画中同时改变多个属性:

view.animate()  
        .scaleX(1)
        .scaleY(1)
        .alpha(1);

image

而对于 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();  

image

使用 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();  

image

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

  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。


本篇学习内容结束,下一篇是自定义View的补充--硬件加速

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,340评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,762评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,329评论 0 329
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,678评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,583评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,995评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,493评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,145评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,293评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,250评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,267评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,973评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,556评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,648评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,873评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,257评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,809评论 2 339

推荐阅读更多精彩内容