在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)
複製代碼
不知不覺,瞎扯了這麼多,好吧 就到這裏了