自定义View 动画 详解-2

属性动画

1.ViewPropertyAnimator

使用方法view.animate().translationX(100);
。。。。。。


image.png

View 的每个方法都对应了 ViewPropertyAnimator 的两个方法,其中一个是带有 -By 后缀的,例如,View.setTranslationX() 对应了 ViewPropertyAnimator.translationX() 和 ViewPropertyAnimator.translationXBy() 这两个方法。其中带有 -By() 后缀的是增量版本的方法,例如,translationX(100) 表示用动画把 View 的 translationX 值渐变为 100,而 translationXBy(100) 则表示用动画把 View 的 translationX 值渐变地增加 100。

2.ObjectAnimator

使用方式:

如果是自定义控件,需要添加 setter / getter 方法;
用 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();  

2.1 setDuration(int duration)设置动画时长 单位:毫秒

例:

    // imageView1: 500 毫秒
    imageView1.animate()  
        .translationX(500)
        .setDuration(500);
        
        // imageView2: 2 秒
    ObjectAnimator animator = ObjectAnimator.ofFloat(  
    imageView2, "translationX", 500);
    animator.setDuration(2000);  
    animator.start();  
2.2 setInterpolator(Interpolator interpolator)设置Interpolator

Interpolator 是一个速度设置器,在参数中填入不同的Interpolator,动画就会以不同的速度模型来执行。
例:

        // imageView1: 线性 Interpolator,匀速
        imageView1.animate()  
        .translationX(500)
        .setInterpolator(new LinearInterpolator());
        
        // imageView: 带施法前摇和回弹的 Interpolator
        ObjectAnimator animator = ObjectAnimator.ofFloat(  
        imageView2, "translationX", 500);
        animator.setInterpolator(new AnticipateOvershootInterpolator());  
        animator.start();  
2.2.1 AccelerateDeceleratelInterpolator 先加速再减速

先加速再减速。这是默认的 Interpolator,也就是说如果你不设置的话,那么动画将会使用这个 Interpolator。

这个是一种最符合现实中物体运动的 Interpolator,它的动画效果看起来就像是物体从速度为 0 开始逐渐加速,然后再逐渐减速直到 0 的运动。它的速度 / 时间曲线以及动画完成度 / 时间曲线都是一条正弦 / 余弦曲线(这句话看完就忘掉就行,没用)。

用途:就像上面说的,它是一种最符合物理世界的模型,所以如果你要做的是最简单的状态变化(位移、放缩、旋转等等),那么一般不用设置 Interpolator,就用这个默认的最好。

2.2.2 LinearInterpolator 匀速
2.2.3 AccelerateInterpolator 持续加速

在整个过程中,动画一直加速,一直持续到动画结束的一瞬间。

用途:它主要用在离场效果中,比如某个物体从界面中飞离,就可以用这种效果。它给人的感觉就会是「这货从零起步,加速飞走了」。到了最后动画骤停的时候,物体已经飞出用户视野,看不到了,所以他们是并不会察觉到这个骤停的。

2.2.4 DecelerateInterpolator 持续减速到0

动画开始的时候是最高速度,然后在动画中逐渐减速,直到动画结束的时候恰好减速到0。

用途:它的效果和上面这个 AccelerateInterpolator 相反,适用场景也和它相反:它主要用于入场效果,比如某个物体从界面的外部飞入界面后停在某处。它给人的感觉会是「咦飞进来个东西,让我仔细看看,哦原来是 XXX」。

2.2.5 AnticipateInterpolator 先拉回一下然后再进行正常动画轨迹。

效果看起来就是 投掷物体或者跳跃等动作前的蓄力。

2.2.6 OvershootInterpolator
2.2.7 AnticipateOvershootinterpolator

动画在开启前回拉,最后超过一些然后回弹。

2.2.8 BounceInterpolator

在目标值处弹跳!

2.2.9 CycleInterpolator

这个也是一个正弦 / 余弦曲线,不过它和 AccelerateDecelerateInterpolator 的区别是,它可以自定义曲线的周期,所以动画可以不到终点就结束,也可以到达终点后回弹,回弹的次数由曲线的周期决定,曲线的周期由 CycleInterpolator() 构造方法的参数决定。

2.2.10 PathInterpolator

自定义动画完成度/时间完成度曲线

用这个 Interpolator 你可以定制出任何你想要的速度模型。定制的方式是使用一个 Path 对象来绘制出你要的动画完成度 / 时间完成度曲线。例如:

        Path interpolatorPath = new Path();
            
        ...
            
        // 匀速
        interpolatorPath.lineTo(1, 1);  
image.png
        Path interpolatorPath = new Path();
            
        ...
            
        // 先以「动画完成度 : 时间完成度 = 1 : 1」的速度匀速运行 25%
        interpolatorPath.lineTo(0.25f, 0.25f);  
        // 然后瞬间跳跃到 150% 的动画完成度
        interpolatorPath.moveTo(0.25f, 1.5f);  
        // 再匀速倒车,返回到目标点
        interpolatorPath.lineTo(1, 1); 
image.png

注意:
这条 Path 描述的其实是一个 y = f(x) (0 ≤ x ≤ 1) (y 为动画完成度,x 为时间完成度)的曲线,所以同一段时间完成度上不能有两段不同的动画完成度(这个好理解吧?因为内容不能出现分身术呀),而且每一个时间完成度的点上都必须要有对应的动画完成度(因为内容不能在某段时间段内消失呀)。所以,下面这样的 Path 是非法的,会导致程序 FC:


image.png

出现重复的动画完成度,即动画内容出现「分身」——程序 FC


image.png

有一段时间完成度没有对应的动画完成度,即动画出现「中断」——程序 FC

Android 5.0 (API 21)引入了三个新的 Interpolator 模型,并把它们加入了 support v4 包中。这三个新的 Interpolator 每个都和之前的某个已有的 Interpolator 规则相似。

2.2.11 FastOutLinearInInterpolator 加速运动

其实它和 AccelerateInterpolator 一样,都是一个持续加速的运动路线。只不过 FastOutLinearInInterpolator 的曲线公式是用的贝塞尔曲线,而 AccelerateInterpolator 用的是指数曲线。具体来说,它俩最主要的区别是 FastOutLinearInInterpolator 的初始阶段加速度比 AccelerateInterpolator 要快一些。

FastOutLinearInInterpolator 速度曲线


image.png

AccelerateInterpolator 速度曲线


image.png

合并


image.png

结论:开发中,想用哪个就用哪个

2.2.12 FastOutSlowInInterpolator 先加速再减速

同样也是先加速再减速的还有前面说过的 AccelerateDecelerateInterpolator,不过它们的效果是明显不一样的。FastOutSlowInInterpolator 用的是贝塞尔曲线,AccelerateDecelerateInterpolator 用的是正弦 / 余弦曲线。具体来讲, FastOutSlowInInterpolator 的前期加速度要快得多

FastOutSlowInInterpolator 速度曲线


image.png

AccelerateDecelerateInterpolator 速度曲线


image.png

不论是从动图还是从曲线都可以看出,这二者比起来,FastOutSlowInInterpolator 的前期加速更猛一些,后期的减速过程的也减得更迅速。用更直观一点的表达就是,AccelerateDecelerateInterpolator 像是物体的自我移动,而 FastOutSlowInInterpolator 则看起来像有一股强大的外力「推」着它加速,在接近目标值之后又「拽」着它减速。总之,FastOutSlowInterpolator 看起来有一点「着急」的感觉。

速度曲线对比


image.png
2.2.13 LinearOutSlowInInterpolator 持续减速

它和 DecelerateInterpolator 比起来,同为减速曲线,主要区别在于 LinearOutSlowInInterpolator 的初始速度更高。

LinearOutSlowInInterpolator 速度曲线


image.png

DecelerateInterpolator 速度曲线


image.png

二者速度曲线比较


image.png

2.3 设置监听器

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

设置监听器的方法, ViewPropertyAnimator 和 ObjectAnimator 略微不一样: ViewPropertyAnimator 用的是 setListener() 和 setUpdateListener() 方法,可以设置一个监听器,要移除监听器时通过 set[Update]Listener(null) 填 null 值来移除;

而 ObjectAnimator 则是用 addListener() 和 addUpdateListener() 来添加一个或多个监听器,移除监听器则是通过 remove[Update]Listener() 来指定移除对象。

另外,由于 ObjectAnimator 支持使用 pause() 方法暂停,所以它还多了一个 addPauseListener() / removePauseListener() 的支持;而 ViewPropertyAnimator 则独有 withStartAction() 和 withEndAction() 方法,可以设置一次性的动画开始或结束的监听。

3.1 ViewPropertyAnimator.setListener() / ObjectAnimator.addListener()

这两个方法的名称不一样,可以设置的监听器数量也不一样,但它们的参数类型都是 AnimatorListener,所以本质上其实都是一样的。 AnimatorListener 共有 4 个回调方法:

3.1.1 onAnimationStart(Animator animation)

当动画开始执行时,这个方法被调用。

3.1.2 onAnimationEnd(Animator animation)

当动画结束时,这个方法被调用。

3.1.3 onAnimationCancel(Animator animation)

当动画被通过 cancel() 方法取消时,这个方法被调用。

需要说明一下的是,就算动画被取消,onAnimationEnd() 也会被调用。所以当动画被取消时,如果设置了 AnimatorListener,那么 onAnimationCancel() 和 onAnimationEnd() 都会被调用。onAnimationCancel() 会先于 onAnimationEnd() 被调用。

3.1.4 onAnimationRepeat(Animator animation)

当动画通过 setRepeatMode() / setRepeatCount() 或 repeat() 方法重复执行时,这个方法被调用。

由于 ViewPropertyAnimator 不支持重复,所以这个方法对 ViewPropertyAnimator 相当于无效。

3.2 ViewPropertyAnimator.setUpdateListener() / ObjectAnimator.addUpdateListener()

和上面 3.1 的两个方法一样,这两个方法虽然名称和可设置的监听器数量不一样,但本质其实都一样的,它们的参数都是 AnimatorUpdateListener。它只有一个回调方法:onAnimationUpdate(ValueAnimator animation)。

3.2.1 onAnimationUpdate(ValueAnimator animation)

当动画的属性更新时(不严谨的说,即每过 10 毫秒,动画的完成度更新时),这个方法被调用。

方法的参数是一个 ValueAnimator,ValueAnimator 是 ObjectAnimator 的父类,也是 ViewPropertyAnimator 的内部实现,所以这个参数其实就是 ViewPropertyAnimator 内部的那个 ValueAnimator,或者对于 ObjectAnimator 来说就是它自己本身。

ValueAnimator 有很多方法可以用,它可以查看当前的动画完成度、当前的属性值等等。不过 ValueAnimator 是下一期才讲的内容,所以这期就不多说了。

3.3 ObjectAnimator.addPauseListener()

由于 ObjectAnimator.pause() 是下期的内容,所以这个方法在这期就不讲了。当然,如果你有兴趣的话,现在就了解一下也可以。

3.4 ViewPropertyAnimator.withStartAction/EndAction()

这两个方法是 ViewPropertyAnimator 的独有方法。它们和 set/addListener() 中回调的 onAnimationStart() / onAnimationEnd() 相比起来的不同主要有两点:

withStartAction() / withEndAction() 是一次性的,在动画执行结束后就自动弃掉了,就算之后再重用 ViewPropertyAnimator 来做别的动画,用它们设置的回调也不会再被调用。而 set/addListener() 所设置的 AnimatorListener 是持续有效的,当动画重复执行时,回调总会被调用。

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

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

推荐阅读更多精彩内容

  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,103评论 1 38
  • 前面学习的内容:Android自定义View(一) -- 初识Android自定义View(二) -- Paint...
    T9的第三个三角阅读 4,317评论 1 19
  • 动画基础概念 动画分类 Android 中动画分为两种,一种是 Tween 动画、还有一种是 Frame 动画。 ...
    Rtia阅读 1,215评论 0 6
  • 1 背景 不能只分析源码呀,分析的同时也要整理归纳基础知识,刚好有人微博私信让全面说说Android的动画,所以今...
    未聞椛洺阅读 2,691评论 0 10
  • 今天是父亲节,按照往年的惯例,我们姐妹会相约回家,虽然在一辈子生活在农村的父亲不懂得这个西方的父亲节,但因为是周末...
    梅花映雪阅读 215评论 12 11