Android动画学习(二):基本属性动画

简述

在上篇文章中,我们介绍了Android中的帧动画和补间动画,本篇内容则将会对Android中另一种动画——属性动画进行简单介绍。
属性动画的功能非常强大,可以满足我们平常对动画的开发。

系列文章:
Android动画学习(一):帧动画和补间动画
Android动画学习(二):基本属性动画
Android动画学习(三):自定义属性动画

和补间动画的区别

上篇内容中,我们讲述了补间动画的简单实现,补间动画主要能够对View实现如下几种操作:

  1. 平移
  2. 旋转
  3. 缩放
  4. 淡入淡出效果

但是补间动画也就只能实现上述4种操作或者是几种操作的混合内容,不能够实现一些其他的动画操作:例如对view的背景颜色进行动画变化。而相对更加灵活的属性动画则可以实现上述功能。

除此之外,补间动画和属性动画还有一个重要的区别:
补间动画只会改变View的显示效果,不会去改变View的真正属性;而属性动画则会确实的改变View的属性

举例来说,当前实现一个平移动画使View1从位置A移动到B。

如果采用补间动画,我们可以看到视觉效果上View1从A移动到了B,当时当你点击在B处的View1时却不会触发View1的点击事件,点击事件的触发仍然是在A处触发的。

如果采用属性动画,则View1的属性会直接变动,包括其点击事件都会在B处触发。

上述就是对于属性动画的基本介绍了,接下来我会介绍一下属性动画的简单使用。

属性动画

1. 属性动画参数

动画原理

在介绍属性动画使用之前,我们先来分析一下动画特性和原理进行分析。

动画的工作原理是不断的去刷新视图,当以特定的高帧频率刷新视图时,在视觉上就会连续效果,和电影原理是一样的。

属性动画同样如此,如下图,实现一个View的X轴平移动作(线性插值):

线性动画示例.jpg

可以看到是视图以每10ms就移动10px(线性插值,动画速度恒定),视图以每10ms就刷新一次。

动画属性

从上面的实例中,我们可以总结下定义一个动画所需要的基本属性:

  1. 动画时长:动画的播放时长
  2. 动画对象:动画应用的对象View
  3. 动画播放逻辑:淡入淡出,缩放,平移等动画的实际播放动作,包括动画播放的先后顺序
  4. 重复和计数:动画是否能够重复,重复次数。
  5. 时间插值:动画播放快慢频率控制。
属性动画核心类

属性动画同样需要这几个条件,主要涉及的核心类包括如下三个:

类名 说明
ValueAnimator 属性动画核心类能够实现属性动画的核心功能,也计算添加动画效果属性的值
ObjectAnimator ValueAnimator子类,用于实现对指定view的动画
AnimatorSet 实现组合动画的类

2. ValueAnimator

ValueAnimator是属性动画中的核心类,它继承自Animator类,类图如下(截自官方文档):

ValueAnimator类图.jpg

类图上可以看到ValueAnimator的几个核心属性和方法:

属性或方法 名称
TimeInterpolator 动画插值
TypeEvaluator 动画评估,指定动画的变化
duration 动画时长
start() 播放动画
startPropertyValue 起始值
endPropertyVaule 结束值
TimeInterpolator

动画插值,决定动画执行的快慢。其中Android SDK中提供了一些常见的插值器,在android.view.animation中,具体内容如下:

说明
AccelerateDecelerateInterpolator 该插值器的变化率在开始和结束时缓慢但在中间会加快。
AccelerateInterpolator 该插值器的变化率在开始时较为缓慢,然后会加快。
AnticipateInterpolator 该插值器先反向变化,然后再急速正向变化。
AnticipateOvershootInterpolator 该插值器先反向变化,再急速正向变化,然后超过定位值,最后返回到最终值。
BounceInterpolator 该插值器的变化会跳过结尾处。
CycleInterpolator 该插值器的动画会在指定数量的周期内重复。
DecelerateInterpolator 该插值器的变化率开始很快,然后减速。
LinearInterpolator 该插值器的变化率恒定不变。
OvershootInterpolator 该插值器会急速正向变化,再超出最终值,然后返回。
TypeEvalutaor

TypeEvalutaor指定动画的变化内容,常见的内容有如下几种:

类/接口 说明
IntEvalutor 计算Int的值变化
FloatEvaluator 计算Float属性的值变化
ArgbEvaluator 计算颜色的值变化
TypeEvaluator 动画变化,可以实现此接口自定义内容
ValueAnimator使用

在ValueAnimator内部使用了一种时间循环机制来计算值与值的动画过度,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就可以完成从初始值平滑的过度到结束值这样的效果。

ValueAnimator的使用并不复杂,截取官方文档上的示例,实现一个在1000ms内从0过度到100的一个数值动画,可以如下代码实现:

    ValueAnimator.ofFloat(0f, 100f).apply {
        duration = 1000
        start()
    }

但是在在执行了上述代码后,却没有看到显示效果,原因是由于这是一个数值动画,不涉及到View的变化,因此如果要获取到动画运行的状态,必须要借助监听器AnimatorUpdateListener来实现,如下:

    ValueAnimator.ofObject(...).apply {
        ...
        addUpdateListener { updatedAnimation ->
            // You can use the animated value in a property that uses the
            // same type as the animation. In this case, you can use the
            // float value in the translationX property.
            textView.translationX = updatedAnimation.animatedValue as Float
        }
        ...
    }

对于ofObject方法需要注意的是此方法的参数可以是多个,我们可以通过设置多个参数实现多个动画节点,如ValueAnimator.ofFloat(0f, 100f,20f,90f),则就会实现从0到100,再到20,最后到90的动画效果。

3. ObjectAnimator

相对于ValueAnimator实现数值的动画过度,能实现View动画的ObjectAnimator类应该我们经常使用到属性动画类。但是实际上ObjectAnimatorValueAnimator的一个子类,其动画生成机制是一样的。

其使用也是比较简单的,示例如下,实现一个文本控件平移动画:

 ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
        duration = 1000
        start()
    }

同样的,我们也能够实现透明度,旋转,缩放的动画效果,如下:

   /**
     * 透明度属性动画
     */
    private fun setAlphaProAnimation(v: View?) {
        if (v != null) {
            val anim = ObjectAnimator.ofFloat(v, "alpha", 0f, 1f, 0f, 1f)
            anim.duration = 3000
            anim.start()
        }
    }

    /**
     * 缩放属性动画
     */
    private fun setScaleProAnimation(v: View?) {
        if (v != null) {
            val anim = ValueAnimator.ofFloat(0f, 1f)
            anim.duration = 3000
            anim.addUpdateListener {
                val value = it.animatedValue
                v.scaleX = value as Float
                v.scaleY = value as Float
            }
            anim.start()
        }
    }

    /**
     * 平移属性动画
     */
    private fun setTranProAnimation(v: View?) {
        if (v != null) {
            val anim = ObjectAnimator.ofFloat(v, "translationX", 0f, 100f)
            anim.duration = 1000
            anim.start()
        }
    }

    /**
     * 旋转属性动画
     */
    private fun setRotationProAnimation(v: View?) {
        if (v != null) {
            val anim = ObjectAnimator.ofFloat(v, "rotation", 0f, 90f)
            anim.duration = 1000
            anim.start()
        }
    }

看到这里,大家可以比较疑惑这个属性的值是从怎么地方获取的,如rotationalpha这些值,实际上这些值是在View的属性里去获取到的,去查询到当前属性名的getset方法,从View的代码中可以看到这些常见的属性名称:

stream.addProperty("drawing:alpha", getAlpha());

相应的,其他常见属性也是如此查询到的。

那么为什么可以通过直接设置View属性就可以设置ObjectAnimator动画呢?

究其原因可以查看源码,其实ObjectAnimator的动画原理是不断的对View对象里的属性进行值的重置得到的,而像alpha这些属性都是View的基本属性,所以直接填写就能够直接更新属性,如果需要设置其他属性变化就需要重新自定义当前View的属性。

4. AnimatorSet组合动画

独立的动画实现的效果是比较有限的,因此在平常的开发过程中将多个动画组合到一起播放是经常碰到的问题。Android SDK中自然也是有方法能够实现组合动画:AnimatorSet

AnimatoeSet类提供了一个play()方法用来播放动画,同时也提供了用来对动画顺序进行管控的方法:

  1. after(Animator anim):将现有动画anim插入到传入的动画之后执行
  2. before:将现有的动画插入到传入的动画之前执行
  3. with:将现有的动画和传入的动画同时执行
  4. after(long time):将 现有的动画延迟指定的时间后执行,单位ms

示例如下:

    val bouncer = AnimatorSet().apply {
        play(bounceAnim).before(squashAnim1)
        play(squashAnim1).with(squashAnim2)
        play(squashAnim1).with(stretchAnim1)
        play(squashAnim1).with(stretchAnim2)
        play(bounceBackAnim).after(stretchAnim2)
    }
    val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
        duration = 250
    }
    AnimatorSet().apply {
        play(bouncer).before(fadeAnim)
        start()
    }

上述示例中的动画执行顺序如下:

  1. 播放动画bounceAnim
  2. 同时播放动画squashAnim1squashAnim2,stretchAnim1,stretchAnim2
  3. 播放bounceBackAnim.
  4. 播放fadeAnim.

5. 动画监听器

可以使用下述监听器来监听动画播放期间的重要事件。

  • Animator.AnimatorListener
    
    • onAnimationStart() - 在动画开始播放时调用。
    • onAnimationEnd() - 在动画结束播放时调用。
    • onAnimationRepeat() - 在动画重复播放时调用。
    • onAnimationCancel() - 在动画取消播放时调用。取消的动画也会调用
    • onAnimationEnd(),动画结束调用。
  • ValueAnimator.AnimatorUpdateListener
    
    • onAnimationUpdate() - 对动画的每一帧调用。

总结

本文对属性动画的概念和属性进行了简单的说明,可以实现和简单的平移、旋转、缩放和透明度控制的简单组合动画,自定义动画后续会进行探讨学习。

参考文章

Google官方文档

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

推荐阅读更多精彩内容