在Android开发过程当中,或多或少确定会与动画打交道,今天我就来聊聊Android动画java
视图动画是仅针对view对象添加动画效果, 仅支持平移(Translate)
、缩放(Scale)
、旋转(Rotate)
、透明度(Alpha)
, 因此你没法对背景颜色等作动画效果android
动画经常使用属性介绍:bash
- android:duration 动画时长(毫秒)
- android:startOffset 动画开始时delay时长(毫秒)
- android:fillAfter 动画结束时,是否保持在最后的位置(默认false,即会恢复初始状态)
- android:fillBefore 动画开始时,在startOffset阶段时是否应用动画属性的初始值,不然应用view的初始值(默认true),须要配合fillEnabled使用,若是fillEnabled为false,则应用动画属性的初始值; 即若是startOffset为0,则fillBefore设不设制 效果都同样
- android:fillEnabled 设置fillBefore属性是否有效
复制代码
注意:fillBefore
和 fillEnabled
对AnimationSet
对象设置不起做用app
利用xml定义动画,则动画文件须要定义在res/anim
文件夹下ide
res/anim/view_anim_ translate.xml
布局
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0" // 定义动画开始时,x轴的位置
android:toXDelta="300" // 定义动画结束时,x轴的位置
android:fromYDelta="0" // 定义动画开始时,y轴的位置
android:toYDelta="0" // 定义动画结束时,y轴的位置
android:duration="2000"/>
复制代码
fromXDelta
、toXDelta
、fromYDelta
、toYDelta
属性能够设置三种类型的值:数值、百分数、百分数p动画
res/anim/view_anim_scale.xml
ui
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="0.5" // 定义动画开始时,水平缩放因子
android:toXScale="2.0" // 定义动画结束时,水平缩放因子
android:fromYScale="1.0" // 定义动画开始时,垂直缩放因子
android:toYScale="1.0" // 定义动画结束时,垂直缩放因子
android:pivotX="0" // 定义原点x轴的位置
android:pivotY="0" // 定义原点y轴的位置
android:duration="2000"/>
复制代码
pivotX
、pivotY
属性也能够设置三种类型的值:数值、百分数、百分数pthis
res/anim/view_anim_rotate.xml
lua
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="10"
android:toDegrees="350"
android:pivotY="50%"
android:pivotX="50%"
android:duration="2000"/>
复制代码
pivotX
、pivotY
属性也能够设置三种类型的值:数值、百分数、百分数p,参考上面的说明
res/anim/view_anim_alpha.xml
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.5" // 动画开始时的透明度
android:toAlpha="1" // 动画结束时的透明度
android:duration="2000"/>
复制代码
上面介绍了 怎么在xml中定义动画文件,而后就须要在代码中使用AnimationUtils
加载动画文件
val translateAnimation = AnimationUtils.loadAnimation(this, R.anim.view_anim_alpha)
view.startAnimation(translateAnimation)
复制代码
上面的四种动画对应的java动画类是TranslateAnimation
、ScaleAnimation
、RotateAnimation
、AlphaAnimation
;因此也能够直接使用java代码来实现上面的四种动画
// 以AlphaAnimation为例
val alphaAnimation = AlphaAnimation(1f, 0.2f)
alphaAnimation.duration = 2000
view.startAnimation(alphaAnimation)
复制代码
AnimationSet
是实现将一组动画一块儿播放的效果
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<translate
android:fromXDelta="0"
android:toXDelta="100%"
android:fromYDelta="0"
android:toYDelta="0"
android:duration="2000"/>
<scale
android:fromXScale="1.0"
android:toXScale="2.0"
android:fromYScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="2000"/>
</set>
复制代码
这里单独在介绍一下fillAfter
、fillBefore
和 fillEnabled
先看看fillAfter
的效果
val translate1Animation = TranslateAnimation(0f, 300f, 0f, 0f)
translate1Animation.fillAfter = false
translate1Animation.duration = 1000
tv_view1.startAnimation(translate1Animation)
val translate2Animation = TranslateAnimation(0f, 300f, 0f, 0f)
translate2Animation.fillAfter = true
translate2Animation.duration = 1000
tv_view2.startAnimation(translate2Animation)
复制代码
可见fillAfter
的做用是在动画结束时,是否保持在最后的位置
在看看fillBefore
配合 fillEnabled
的效果
val translate1Animation = TranslateAnimation(100f, 300f, 0f, 0f)
translate1Animation.isFillEnabled = true
translate1Animation.fillBefore = false
translate1Animation.fillAfter = true
translate1Animation.startOffset = 1000
translate1Animation.duration = 1000
tv_view1.startAnimation(translate1Animation)
val translate2Animation = TranslateAnimation(100f, 300f, 0f, 0f)
translate2Animation.isFillEnabled = true
translate2Animation.fillBefore = true
translate2Animation.fillAfter = true
translate2Animation.startOffset = 1000
translate2Animation.duration = 1000
tv_view2.startAnimation(translate2Animation)
复制代码
可见fillBefore
表示在startOffset
阶段时是否应用动画属性的初始值
属性动画
没有上面视图动画的限制,几乎能够为任何内容添加动画效果;您能够定义一个随时间更改任何对象属性的动画,不管其是否绘制到屏幕上
属性动画
没有fillAfter
、fillBefore
和 fillEnabled
; 动画结束后保持在最后一帧的位置(相似视图动画的fillAfter
为true
效果同样),动画真正开始前(即startDelay
结束后) 才应用动画的初始值(相似视图动画的fillEnabled
为true
和fillBefore
为false
效果同样)
属性动画
没有startOffset
,而是使用startDelay
代替
ValueAnimator
类是属性动画的核心类,它继承自Animator
;借助ValueAnimator
类,您能够为动画播放期间某些类型的值添加动画效果,只需指定一组要添加动画效果的 int
、float
或颜色值
便可, 可使用ValueAnimator
的任一工厂方法来获取它:ofInt()
、ofFloat()
、ofObject()
、ofArgb()
;
因为ValueAnimator
类只提供计算添加动画效果以后的值,因此须要借助AnimatorUpdateListener实现修改view的属性,实现动画效果
// 平移
ValueAnimator.ofFloat(0f, 300f).apply {
duration = 1000
addUpdateListener {
view.translationX = it.animatedValue as Float
view.translationY = it.animatedValue as Float
}
start()
}
// 缩放
ValueAnimator.ofFloat(0.5f, 2f).apply {
duration = 1000
addUpdateListener {
view.pivotX = 0f
view.pivotY = 0f
view.scaleX = it.animatedValue as Float
view.scaleY = it.animatedValue as Float
}
start()
}
// 旋转
ValueAnimator.ofFloat(10f, 350f).apply {
duration = 1000
addUpdateListener {
view.pivotX = 0f
view.pivotY = 0f
view.rotation = it.animatedValue as Float
}
start()
}
// 透明度
ValueAnimator.ofFloat(0.1f, 1f).apply {
duration = 1000
addUpdateListener {
view.alpha = it.animatedValue as Float
}
start()
}
// 背景颜色
ValueAnimator.ofArgb(Color.parseColor("#ff0000"), Color.parseColor("#0000ff")).apply {
duration = 1000
addUpdateListener {
view.setBackgroundColor(it.animatedValue as Int)
}
start()
}
复制代码
可见ValueAnimator
是给定一个数值的变化区间和时间段(默认 300 毫秒),计算播放期间的的动画值,而后由你本身实现修改view的属性,因此ValueAnimator
能够根据须要实现修改view的任何属性,达到不一样的效果
对于属性动画的pivotX
和pivotY
与 视图动画有一点不同,只能设置数值,不能设置百分比等;pivotX
和pivotY
默认是相对view的中心位置,一旦设置就是相对view的左上角的坐标
上面是使用代码建立属性动画,下面咱们来使用xml定义属性动画
对于属性动画
在xml中定义动画文件与视图动画
不同,视图动画
是定义在res/anim
文件夹下, 而属性动画
定义在res/animator
文件夹下
res/animator/view_translate.xml
<?xml version="1.0" encoding="utf-8"?>
// animator 标签 对应 ValueAnimator类
// objectAnimator 标签 对应下面要讲到的 ObjectAnimator类
<animator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0"
android:valueTo="300"
android:valueType="floatType"
android:duration="1000" />
复制代码
在代码中使用以下
(AnimatorInflater.loadAnimator(this, R.animator.view_translate) as ValueAnimator).apply {
addUpdateListener {
view.translationX = it.animatedValue as Float
view.translationY = it.animatedValue as Float
}
start()
}
复制代码
ObjectAnimator
是ValueAnimator
的子类,它也有ofInt()
、ofFloat()
、ofObject()
、ofArgb()
、ofPropertyValuesHolder
等工厂方法,可是与ValueAnimator
不同的是,新增了target
、和 propertyName
参数
其中target
和 propertyName
的关系是 target
必须有一个public
的setXXX
的方法, 其中XXX
就是PropertyName
的名字
上面的demo使用ObjectAnimator
以下:
// 平移
ObjectAnimator.ofFloat(view, "translationX", 0f, 300f).apply {
duration = 1000
start()
}
// 缩放
ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 2f).apply {
duration = 1000
view.pivotX = 0f
view.pivotY = 0f
start()
}
// 缩放(同时修改2个属性 方法一)
val path = Path().apply {
moveTo(0.5f, 0.5f)
lineTo(2f, 2f)
}
ObjectAnimator.ofFloat(view, "scaleX", "scaleY", path).apply {
duration = 1000
view.pivotX = 0f
view.pivotY = 0f
start()
}
// 缩放(同时修改2个属性 方法二)
val scaleXAnim = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 2f)
val scaleYAnim = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 2f)
ObjectAnimator.ofPropertyValuesHolder(view, scaleXAnim, scaleYAnim).apply {
duration = 1000
view.pivotX = 0f
view.pivotY = 0f
start()
}
// 旋转
ObjectAnimator.ofFloat(view, "rotation", 10f, 350f).apply {
duration = 1000
view.pivotX = 0f
view.pivotY = 0f
start()
}
// 透明度
ObjectAnimator.ofFloat(view, "alpha", 0.1f, 1f).apply {
duration = 1000
start()
}
// 背景颜色
ObjectAnimator.ofArgb(view, "backgroundColor", Color.parseColor("#ff0000"), Color.parseColor("#0000ff")).apply {
duration = 1000
start()
}
复制代码
xml中定义动画资源文件以下
res/animator/view_scale.xml
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:propertyName="scaleX"
android:valueFrom="0.5"
android:valueTo="2.0"
android:valueType="floatType" />
复制代码
代码中加载动画文件以下
AnimatorInflater.loadAnimator(this, R.animator.view_scale).apply {
setTarget(view)
start()
}
复制代码
大多数状况下 咱们都是使用ObjectAnimator
,而不是ValueAnimator
AnimatorSet
是 针对属性动画 用于播放一组动画效果的类;它支持同时播放(playTogether)
、顺序播放(playSequentially)
; 同时也能够按照喜欢使用before(anim)
、with(anim)
、after(anim)
三个方法自由组合多个属性动画
with(anim)
设置此动画play(anim)
与anim同时执行
before(anim)
设置此动画play(anim)
在anim以前执行
after(anim)
设置此动画play(anim)
在anim以后执行
val translationXAnim = ObjectAnimator.ofFloat(tv_view, "translationX", 0f, 300f).apply { duration = 1000 }
val scaleXAnim = ObjectAnimator.ofFloat(tv_view, "scaleX", 0.5f, 2f).apply { duration = 1000 }
val scaleYAnim = ObjectAnimator.ofFloat(tv_view, "scaleY", 0.5f, 2f).apply { duration = 1000 }
AnimatorSet().apply {
// 若是设置了duration,则会覆盖每一个animator的duration,不然使用animator本身的duration
// duration = 4000
// 同时播放
// playTogether(translationXAnim, scaleXAnim, scaleYAnim)
// 按顺序播放
// playSequentially(translationXAnim, scaleXAnim, scaleYAnim)
// 先平移,而后在x轴和y轴同时缩放
play(translationXAnim).before(scaleXAnim).before(scaleYAnim)
start()
}
复制代码
在xml中定义动画资源文件以下
res/animator/view_animator_set.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="sequentially"> <!-- 定义按循序播放 -->
<objectAnimator
android:duration="1000"
android:propertyName="translationX"
android:valueType="floatType"
android:valueFrom="0"
android:valueTo="300" />
<!-- 定义一块儿播放播放 -->
<set android:ordering="together">
<objectAnimator
android:duration="1000"
android:propertyName="scaleX"
android:valueType="floatType"
android:valueFrom="0.5"
android:valueTo="2.0"/>
<objectAnimator
android:duration="1000"
android:propertyName="scaleY"
android:valueType="floatType"
android:valueFrom="0.5"
android:valueTo="2.0"/>
</set>
</set>
复制代码
使用代码加载动画资源文件以下
AnimatorInflater.loadAnimator(this, R.animator.view_animator_set).apply {
setTarget(view)
start()
}
复制代码
LayoutTransition
类为 ViewGroup
内的布局更改添加动画效果, 当您向 ViewGroup
添加视图或删除其中的视图时,或当您使用 VISIBLE
、INVISIBLE
或 GONE
调用视图的 setVisibility() 方法时,这些视图可能会经历出现和消失动画。向 ViewGroup
添加视图或删除其中的视图时,其中剩余的视图还可能以动画形式移动到新位置
您能够调用 setAnimator()
并使用如下任一 LayoutTransition
常量传入 Animator
对象,从而在 LayoutTransition
对象中定义相应动画
APPEARING
: 定义在ViewGroup中添加或显示view的动画CHANGE_APPEARING
: 定义因为view的添加或显示,致使ViewGroup中其余view发生变化的动画DISAPPEARING
: 定义在ViewGroup中隐藏或移除view的动画CHANGE_DISAPPEARING
: 定义因为view的隐藏或移除,致使ViewGroup中其余view发生变化的动画先看看下面的demo
第一步先定义layout.xml以下
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onBlock1Show"
android:text="显示"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onBlock1Hide"
android:text="隐藏"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/block1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:textSize="30dp"
android:textColor="#ff0000"
android:gravity="center"
android:text="block1"
android:background="#dddd00"/>
<TextView
android:id="@+id/block2"
android:layout_width="match_parent"
android:layout_height="100dp"
android:textSize="30dp"
android:textColor="#ff0000"
android:gravity="center"
android:text="block2"
android:background="#00fdfd"/>
</LinearLayout>
</LinearLayout>
复制代码
而后在代码中使用LayoutTransition
以下
先看看LayoutTransition.DISAPPEARING
和LayoutTransition.APPEARING
的效果
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animator_property)
// 定义block1隐藏的动画
val scaleYOut = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f)
val scaleXOut = PropertyValuesHolder.ofFloat("scaleY", 1f, 0f)
val alphaOut = PropertyValuesHolder.ofFloat("alpha", 1f, 0f)
val rotationOut = PropertyValuesHolder.ofFloat("rotation", 0f, 360f)
val disappearingAnim = ObjectAnimator.ofPropertyValuesHolder(null as? Any, scaleYOut, scaleXOut, alphaOut, rotationOut)
// 定义block1显示的动画
val scaleYIn = PropertyValuesHolder.ofFloat("scaleY", 0f, 1f)
val scaleXIn = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f)
val alphaIn = PropertyValuesHolder.ofFloat("alpha", 0f, 1f)
val rotationIn = PropertyValuesHolder.ofFloat("rotation", 360f, 0f)
val appearingAnim = ObjectAnimator.ofPropertyValuesHolder(null as? Any, scaleYIn, scaleXIn, alphaIn, rotationIn)
val layoutTransition = LayoutTransition()
// 应用block1隐藏动画
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnim)
// 应用block1显示动画
layoutTransition.setAnimator(LayoutTransition.APPEARING, appearingAnim)
block_container.layoutTransition = layoutTransition
}
fun onBlock1Show(view: View) {
block1?.parent ?: block_container.addView(block1, 0)
// block1.visibility = View.VISIBLE
}
fun onBlock1Hide(view: View) {
block_container.removeView(block1)
// block1.visibility = View.GONE
}
复制代码
再看看LayoutTransition.CHANGE_DISAPPEARING
和LayoutTransition.CHANGE_APPEARING
的效果
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_animator_property)
// 定义block1隐藏的动画
...
// 定义block1显示的动画
...
// 定义隐藏block1时,block2的动画
// 必定要添加下面的动画(LayoutTransition 源码里是这么写的),不然添加的动画可能没效果, 暂时不知道为何
val pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1)
val pvhTop = PropertyValuesHolder.ofInt("top", 0, 1)
val pvhRight = PropertyValuesHolder.ofInt("right", 0, 1)
val pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1)
val pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1)
val pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1)
// 自定义缩放动画
val scaleXInAnim = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.2f, 1f)
val scaleYInAnim = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.2f, 1f)
val changeDisappearingAnim = ObjectAnimator.ofPropertyValuesHolder(null as? Any, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY, scaleXInAnim, scaleYInAnim)
// 定义显示block1时,block2的动画
val backgroundAnim = ObjectAnimator.ofArgb(null as? Any, "backgroundColor", Color.parseColor("#ff0000"), Color.parseColor("#0000ff"))
val changingAppearingAnim = AnimatorSet().apply {
//(偷个懒,直接克隆changeDisappearingAnim动画)
playTogether(changeDisappearingAnim.clone(), backgroundAnim)
}
val layoutTransition = LayoutTransition()
// 应用block1隐藏动画
layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, disappearingAnim)
// 应用block1显示动画
layoutTransition.setAnimator(LayoutTransition.APPEARING, appearingAnim)
// 应用block2的changeDisappearingAnim动画
layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, changeDisappearingAnim)
// 应用block2的changingAppearingAnim动画
layoutTransition.setAnimator(LayoutTransition.CHANGE_APPEARING, changingAppearingAnim)
block_container.layoutTransition = layoutTransition
}
复制代码
效果以下
通常状况不须要设置LayoutTransition.CHANGE_DISAPPEARING
和LayoutTransition.CHANGE_APPEARING
的动画,直接使用默认的动画便可;若是须要自定义动画,则必定要在默认的属性动画上,在加上你本身的动画,相似上面的代码
上面是使用代码定义动画资源,你也能够在res/animator
文件夹下中定义xml动画资源,而后在代码中应用, 这里我就不举例了
除了自定义LayoutTransition
以外,咱们能够直接使用animateLayoutChanges
属性快速的添加默认的动画效果
<LinearLayout
android:id="@+id/block_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:animateLayoutChanges="true">
...
</LinearLayout>
复制代码
这样的话就不须要在代码里添加任何代码,自动为添加到 ViewGroup 或从中删除的视图以及该 ViewGroup 中剩余的视图添加动画效果
经过StateListAnimator
类,您能够定义在视图状态更改时运行的Animator
。此对象充当 Animator
对象的封装容器,只要指定的视图状态(例如“按下”或“聚焦”)发生更改,就会调用该动画
StateListAnimator
最低支持Android 5.0
在drawable下定义资源文件以下(也能够定义在xml文件夹下,官方文档是定义在xml文件夹下)
res/drawable/animator_state_list
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1.5"
android:valueType="floatType"/>
<objectAnimator android:propertyName="rotation"
android:duration="@android:integer/config_shortAnimTime"
android:valueFrom="0"
android:valueTo="360"
android:valueType="floatType"/>
</set>
</item>
<item android:state_pressed="false">
<set>
<objectAnimator android:propertyName="scaleX"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
<objectAnimator android:propertyName="scaleY"
android:duration="@android:integer/config_shortAnimTime"
android:valueTo="1"
android:valueType="floatType"/>
<objectAnimator android:propertyName="rotation"
android:duration="@android:integer/config_shortAnimTime"
android:valueFrom="360"
android:valueTo="0"
android:valueType="floatType"/>
</set>
</item>
</selector>
复制代码
而后在使用stateListAnimator
属性设置animator_state_list
资源文件便可
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<Button
android:id="@+id/btn_state_list"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:text="StateListAnimator"
android:stateListAnimator="@drawable/animator_state_list"/>
</LinearLayout>
复制代码
也能够在代码中设置
val stateListAnimator = AnimatorInflater.loadStateListAnimator(this, R.drawable.animator_state_list)
btn_state_list.stateListAnimator = stateListAnimator
复制代码
效果以下
AnimatedStateListDrawable
的做用是 在状态更改间播放一组关键帧动画,达到最终的动画效果
AnimatedStateListDrawable
最低支持Android 5.0
因为没有那么多图片,因此下面的demo以color为例(drawable也支持设置color)
首先在在res/drawable
文件夹下新建animator_state_list_drawable.xml
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 定义 按下 状态的 drawable -->
<item android:id="@+id/pressed" android:state_pressed="true" android:drawable="@color/color_00ff00"/>
<!-- 定义 松开 状态的 drawable -->
<item android:id="@+id/no_pressed" android:state_pressed="false" android:drawable="@color/color_ffff00"/>
<!-- 定义 由按下到松开 的动画转换过程(相似帧动画, 一帧一帧的播放) -->
<transition
android:fromId="@id/pressed"
android:toId="@id/no_pressed">
<animation-list>
<item android:duration="30" android:drawable="@color/color_00ff00"/>
<item android:duration="30" android:drawable="@color/color_11ff00"/>
<item android:duration="30" android:drawable="@color/color_22ff00"/>
<item android:duration="30" android:drawable="@color/color_33ff00"/>
<item android:duration="30" android:drawable="@color/color_44ff00"/>
<item android:duration="30" android:drawable="@color/color_55ff00"/>
<item android:duration="30" android:drawable="@color/color_66ff00"/>
<item android:duration="30" android:drawable="@color/color_77ff00"/>
<item android:duration="30" android:drawable="@color/color_88ff00"/>
<item android:duration="30" android:drawable="@color/color_99ff00"/>
<item android:duration="30" android:drawable="@color/color_aaff00"/>
<item android:duration="30" android:drawable="@color/color_bbff00"/>
<item android:duration="30" android:drawable="@color/color_ccff00"/>
<item android:duration="30" android:drawable="@color/color_ddff00"/>
<item android:duration="30" android:drawable="@color/color_eeff00"/>
<item android:duration="30" android:drawable="@color/color_ffff00"/>
</animation-list>
</transition>
<!-- 定义 由松开到按下 的动画转换过程(相似帧动画, 一帧一帧的播放) -->
<transition
android:fromId="@id/no_pressed"
android:toId="@id/pressed">
<animation-list>
<item android:duration="30" android:drawable="@color/color_ffff00"/>
<item android:duration="30" android:drawable="@color/color_eeff00"/>
<item android:duration="30" android:drawable="@color/color_ddff00"/>
<item android:duration="30" android:drawable="@color/color_ccff00"/>
<item android:duration="30" android:drawable="@color/color_bbff00"/>
<item android:duration="30" android:drawable="@color/color_aaff00"/>
<item android:duration="30" android:drawable="@color/color_99ff00"/>
<item android:duration="30" android:drawable="@color/color_88ff00"/>
<item android:duration="30" android:drawable="@color/color_77ff00"/>
<item android:duration="30" android:drawable="@color/color_66ff00"/>
<item android:duration="30" android:drawable="@color/color_55ff00"/>
<item android:duration="30" android:drawable="@color/color_44ff00"/>
<item android:duration="30" android:drawable="@color/color_33ff00"/>
<item android:duration="30" android:drawable="@color/color_22ff00"/>
<item android:duration="30" android:drawable="@color/color_11ff00"/>
<item android:duration="30" android:drawable="@color/color_00ff00"/>
</animation-list>
</transition>
</animated-selector>
复制代码
而后在layout布局中使用
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<View
android:id="@+id/v_state_list_drawable"
android:layout_width="200dp"
android:layout_height="200dp"
android:clickable="true"
android:background="@drawable/animator_state_list_drawable"/>
</LinearLayout>
复制代码
效果以下
相信你们对于在res/drawable
下定义和使用普通的drawable
应该都知道,而AnimatedStateListDrawable
只是添加了一组关键帧的过渡动画,让视图的状态变化体验更友好,不显得那么僵硬而已
TimeInterpolator(插值器)
指定了如何根据时间计算动画中的因子(fraction)
,它的取值范围通常是0到1
TimeInterpolator(插值器)
会接受来自Animator
提供的已播放动画的时间片断(input)
;时间片断(input)
的取值范围是0到1;而TimeInterpolator(插值器)
就是修改这个值达获得最终的因子(fraction)
,从而达到LinearInterpolator(线性)
、AccelerateInterpolator(加速)
、DecelerateInterpolator(减速)
、AccelerateDecelerateInterpolator(先加速再减速)
等不一样的动画效果
LinearInterpolator
源码
override fun getInterpolation(input: Float): Float = input
复制代码
AccelerateDecelerateInterpolator
源码
override fun getInterpolation(input: Float): Float =
(Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f
复制代码
TimeInterpolator(插值器)都是基于数学中一个二维坐标系来计算的;若是你要实现一个很是『真实、天然』的动画,那你必须知道对应的数学公式
TypeEvaluator(估值器)
就是根据TimeInterpolator(插值器)
计算出来的因子(fraction)
,而后根据初始值(startValue)
和结束值(endValue)
, 计算一个最终的属性值
系统只提供了IntEvaluator
、FloatEvaluator
和 ArgbEvaluator
三种类型的估值器,若是要为 Android 系统没法识别的类型添加动画效果,则能够经过实现TypeEvaluator
接口来建立您本身的估值器
FloatEvaluator
源码
// 因为fraction 取值范围是0~1
// 当 fraction = 0时,计算出来的属性值就是 startValue
// 当 fraction = 1时,计算出来的属性值就是 endValue
// 当 fraction 在 0~1中间时,计算出来的属性值就是 startValue~endValue的中间值
override fun evaluate(fraction: Float, startValue: Number, endValue: Number): Float {
val startFloat = startValue.toFloat()
return startFloat + fraction * (endValue.toFloat() - startFloat)
}
复制代码
Keyframe
对象由<时间因子, 值>对组成,用于在动画的特定时间定义特定的状态, 每一个关键帧还能够用本身的插值器控制动画在上一关键帧时间和此关键帧时间之间的时间间隔内的行为
Keyframe
提供了 ofInt()
、ofFloat()
或 ofObject()
工厂方法建立实例
// 定义刚开始时 0度
val kf0 = Keyframe.ofFloat(0f, 0f)
// 定义时间因子factor=0.5时,value = 360
// 即factor在[0f, 0.5f]变化过程当中,rotation从0变到360
val kf1 = Keyframe.ofFloat(0.5f, 360f)
// 定义时间因子factor=1时,value = 0
// 即factor在[0.5f, 1f]变化过程当中,rotation从360变到0
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
duration = 3000
start()
}
复制代码
ViewPropertyAnimator
是使用单个Animator
对象轻松为 View 的多个属性并行添加动画效果;
ViewPropertyAnimator
它会修改视图属性的实际值,但在同时为多个属性添加动画效果时,它更为高效
ViewPropertyAnimator
代码更加简洁,也更易读
下面咱们看看它跟ObjectAnimator
的使用区别
多个ObjectAnimator
对象
val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
playTogether(animX, animY)
start()
}
复制代码
一个ObjectAnimator
对象
val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()
复制代码
ViewPropertyAnimator
myView.animate().x(50f).y(100f)
复制代码
不知不觉,瞎扯了这么多,好吧 就到这里了