Android代碼實(shí)現(xiàn)新年賀卡動畫示例詳解
引言
什么?兔了個(gè)兔?吐了還要吐?首先今天,我們自己用android程序?qū)崿F(xiàn)一個(gè)兔年的新年賀卡。下面就是見證美好的時(shí)刻,上效果。
好,我們來使用Android動畫的知識,來實(shí)現(xiàn)這樣一個(gè)動畫效果吧。
需要使用到的知識點(diǎn)
架構(gòu)設(shè)計(jì)、Android視圖動畫、TypeEvaluator、Path、組合模式、代理模式。
思路分析
我們回顧動畫的種類,補(bǔ)間動畫、幀動畫、屬性動畫以及Android View自帶的視圖動畫。我們今天自己基于屬性動畫來打造一個(gè)山寨版的Android視圖動畫吧。我們可以從平移動畫、縮放動畫、旋轉(zhuǎn)動畫和透明度動畫中抽象出一個(gè)基類Action類。我是不會告訴你這個(gè)類的命名我是抄的cocos2d的。然后我們擴(kuò)展Action類,實(shí)現(xiàn)這四種動畫,再作用在View上。這樣就可以讓View按我們的動畫框架播放動畫了。
代碼實(shí)現(xiàn)
/** * 組合的action可以直接交給view執(zhí)行。 */ interface Action<A : Action<A>> { fun add(action: A): A fun getAnimator(): Animator<A> fun startAnimation(view: View, duration: Long) }
抽象一個(gè)Action接口,Action還可以添加Action,這里是組合模式的結(jié)構(gòu)。
import android.view.View import dora.widget.animator.AlphaAnimator import dora.widget.animator.Animator class AlphaAction(val alpha: Float) : Action<AlphaAction> { private var animator = AlphaAnimator() override fun add(action: AlphaAction): AlphaAction { animator.add(action) return this } override fun startAnimation(view: View, duration: Long) { animator.startAnimation(view, duration) } override fun getAnimator(): Animator<AlphaAction> { return animator } operator fun plus(action: AlphaAction) = add(action) init { animator.add(this) } }
我們以透明度動畫為例,在Animator中實(shí)現(xiàn)屬性動畫的邏輯,然后聚合到Action類的實(shí)現(xiàn),通過代理的方式調(diào)用我們的動畫實(shí)現(xiàn)。這里我們重寫了+號操作符,這樣可以支持兩個(gè)對象進(jìn)行相加,這個(gè)是Kotlin模仿C++的語法。
import android.view.View import dora.widget.action.Action import java.util.* abstract class Animator<A : Action<A>>: Action<A> { protected lateinit var targetView: View protected var actionTree: MutableList<A> = ArrayList() override fun add(action: A): A { actionTree.add(action) return actionTree[actionTree.size - 1] } override fun startAnimation(view: View, duration: Long) { targetView = view } override fun getAnimator(): Animator<A> { return this } }
在Animator中,將所有的Action放到一個(gè)List集合中保存起來,當(dāng)我們調(diào)用startAnimation()方法,則可以將傳入的View拿到,并執(zhí)行動畫。
class AlphaAnimator : Animator<AlphaAction>() { override fun startAnimation(view: View, duration: Long) { super.startAnimation(view, duration) actionTree.add(0, AlphaAction(1.0f)) val animator = ObjectAnimator.ofObject( this, ALPHA, AlphaEvaluator(), *actionTree.toTypedArray() ) animator.duration = duration animator.start() } fun setAlpha(action: AlphaAction) { val alpha = action.alpha targetView.alpha = alpha } private class AlphaEvaluator : TypeEvaluator<AlphaAction> { override fun evaluate( fraction: Float, startValue: AlphaAction, endValue: AlphaAction ): AlphaAction { val action: AlphaAction val startAlpha = startValue.alpha val endAlpha = endValue.alpha action = if (endAlpha > startAlpha) { AlphaAction(startAlpha + fraction * (endAlpha - startAlpha)) } else { AlphaAction(startAlpha - fraction * (startAlpha - endAlpha)) } return action } } companion object { private const val ALPHA = "alpha" } override fun getAnimator(): Animator<AlphaAction> { return this } }
比如AlphaAnimator的實(shí)現(xiàn),我們這里最關(guān)鍵的一行代碼就是使用了ObjectAnimator,用它來監(jiān)聽該對象屬性的變化。比如這里我們監(jiān)聽alpha屬性實(shí)際上是監(jiān)聽的setAlpha方法。動畫變化的中間值則是通過TypeEvaluator估值器來進(jìn)行計(jì)算估值的。在startAnimation()方法被調(diào)用的時(shí)候,我們默認(rèn)在最前面添加了一個(gè)默認(rèn)值。
actionTree.add(0, AlphaAction(1.0f))
我這里只是拋磚引玉,你可以做得更好,比如將初始狀態(tài)不要寫死,讓子類去指定或在使用的時(shí)候動態(tài)指定,這樣就會更加的靈活。
abstract class PathAction internal constructor( val x: Float, val y: Float ) : Action<PathAction> { private var animator = PathAnimator() override fun add(action: PathAction): PathAction { animator.add(action) return this } override fun startAnimation(view: View, duration: Long) { animator.startAnimation(view, duration) } override fun getAnimator(): Animator<PathAction> { return animator } operator fun plus(action: PathAction) = add(action) init { animator.add(this) } }
移動的動畫也是類似的邏輯,我們基于Path實(shí)現(xiàn)移動動畫。
class PathAnimator : Animator<PathAction>() { private val PATH = "path" override fun startAnimation(view: View, duration: Long) { super.startAnimation(view, duration) actionTree.add(0, MoveTo(0f, 0f)) val animator = ObjectAnimator.ofObject( this, PATH, PathEvaluator(), *actionTree.toTypedArray() ) animator.duration = duration animator.start() } fun setPath(action: MoveTo) { val x = action.x val y = action.y targetView.translationX = x targetView.translationY = y } private inner class PathEvaluator : TypeEvaluator<PathAction> { override fun evaluate(fraction: Float, startValue: PathAction, endValue: PathAction): PathAction { var x = 0f var y = 0f if (endValue is MoveTo) { x = endValue.x y = endValue.y } if (endValue is LineTo) { x = startValue.x + fraction * (endValue.x - startValue.x) y = startValue.y + fraction * (endValue.y - startValue.y) } val ratio = 1 - fraction if (endValue is QuadTo) { x = Math.pow(ratio.toDouble(), 2.0) .toFloat() * startValue.x + (2 * fraction * ratio * (endValue).inflectionX) + (Math.pow( endValue.x.toDouble(), 2.0 ) .toFloat() * Math.pow(fraction.toDouble(), 2.0).toFloat()) y = Math.pow(ratio.toDouble(), 2.0) .toFloat() * startValue.y + (2 * fraction * ratio * (endValue).inflectionY) + (Math.pow( endValue.y.toDouble(), 2.0 ) .toFloat() * Math.pow(fraction.toDouble(), 2.0).toFloat()) } if (endValue is CubicTo) { x = Math.pow(ratio.toDouble(), 3.0).toFloat() * startValue.x + (3 * Math.pow( ratio.toDouble(), 2.0 ).toFloat() * fraction * (endValue).inflectionX1) + (3 * ratio * Math.pow(fraction.toDouble(), 2.0).toFloat() * (endValue).inflectionX2) + Math.pow(fraction.toDouble(), 3.0) .toFloat() * endValue.x y = Math.pow(ratio.toDouble(), 3.0).toFloat() * startValue.y + (3 * Math.pow( ratio.toDouble(), 2.0 ).toFloat() * fraction * (endValue).inflectionY1) + (3 * ratio * Math.pow(fraction.toDouble(), 2.0).toFloat() * (endValue).inflectionY2) + Math.pow(fraction.toDouble(), 3.0) .toFloat() * endValue.y } return MoveTo(x, y) } } override fun getAnimator(): Animator<PathAction> { return this } }
曲線運(yùn)動則牽扯到一些貝瑟爾曲線的知識。 比如二階的貝瑟爾曲線
class QuadTo(val inflectionX: Float, val inflectionY: Float, x: Float, y: Float) : PathAction(x, y)
和三階的貝瑟爾曲線
class CubicTo( val inflectionX1: Float, val inflectionX2: Float, val inflectionY1: Float, val inflectionY2: Float, x: Float, y: Float ) : PathAction(x, y)
直線運(yùn)動則是定義了MoveTo和LineTo兩個(gè)類。
class MoveTo(x: Float, y: Float) : PathAction(x, y)
class LineTo(x: Float, y: Float) : PathAction(x, y)
調(diào)用動畫框架API
我們賀卡的動畫就是使用了以下的寫法,同一類Action可以通過+號操作符進(jìn)行合并,我們可以同時(shí)調(diào)用這四類Action進(jìn)行動畫效果的疊加,這樣可以讓動畫效果更加豐富。
(AlphaAction(0.2f) + AlphaAction(1f)).startAnimation(ivRabbit, 2000) (MoveTo(-500f, 100f) + LineTo(-400f, 80f) + LineTo(-300f, 50f) + LineTo(-200f, 100f) + LineTo(-100f, 80f) + LineTo(0f, 100f) + LineTo(100f, 80f) + LineTo(200f, 50f) + LineTo(300f, 100f) + LineTo(400f, 80f) ) .startAnimation(ivRabbit, 2000) (RotateAction(0f) + RotateAction(180f)+ RotateAction(360f)) .startAnimation(ivRabbit, 4000) ScaleAction(2f, 2f).startAnimation(ivRabbit, 8000) Handler().postDelayed({ MoveTo(0f, 0f).startAnimation(ivRabbit, 500) }, 8000)
興趣是最好的老師,本文篇幅有限,我們可以通過Android的代碼在Android手機(jī)上實(shí)現(xiàn)各種各樣炫酷的效果。跟著哆啦一起玩轉(zhuǎn)Android自定義View吧。
以上就是Android代碼實(shí)現(xiàn)新年賀卡動畫示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Android新年賀卡動畫的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
學(xué)習(xí)Android自定義Spinner適配器
這篇文章主要為大家詳細(xì)介紹了學(xué)習(xí)Android自定義Spinner適配器的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-05-05Android加載對話框同時(shí)異步執(zhí)行實(shí)現(xiàn)方法
Android中通過子線程連接網(wǎng)絡(luò)獲取資料,同時(shí)顯示加載進(jìn)度對話框給用戶的操作2012-11-11神經(jīng)網(wǎng)絡(luò)API、Kotlin支持,那些你必須知道的Android 8.1預(yù)覽版和Android Studio 3.0新特
這篇文章主要介紹了神經(jīng)網(wǎng)絡(luò)API、Kotlin支持,那些你必須了解的Android 8.1預(yù)覽版和Android Studio 3.0新特性,需要的朋友可以參考下2017-10-10Android設(shè)備間實(shí)現(xiàn)藍(lán)牙(Bluetooth)共享上網(wǎng)
這篇文章主要為大家詳細(xì)介紹了Android設(shè)備間實(shí)現(xiàn)藍(lán)牙(Bluetooth)共享上網(wǎng)的方法,主要以圖片的方式向大家展示藍(lán)牙共享上網(wǎng)2016-03-03Android實(shí)現(xiàn)頁面跳轉(zhuǎn)的全過程記錄
對于android軟件開發(fā)初級學(xué)習(xí)者來說,簡單的頁面跳轉(zhuǎn)是必學(xué)的,這篇文章主要給大家介紹了關(guān)于Android實(shí)現(xiàn)頁面跳轉(zhuǎn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10Android 進(jìn)入設(shè)備后臺data文件夾的辦法
Android 進(jìn)入設(shè)備后臺data文件夾的辦法,需要的朋友可以參考一下2013-05-05Android使用第三方庫實(shí)現(xiàn)日期選擇器
這篇文章主要為大家詳細(xì)介紹了Android使用第三方庫實(shí)現(xiàn)日期選擇器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10Android 使用<layer-list>實(shí)現(xiàn)微信聊天輸入框功能
<layer-list> 標(biāo)簽可以設(shè)置LayerDrawable,一種有層次的Drawable疊加效果,<layer-list> 可以包含多個(gè) <item>標(biāo)簽。這篇文章主要介紹了Android 使用<layer-list>實(shí)現(xiàn)微信聊天輸入框,需要的朋友可以參考下2017-05-05