<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
什麼?兔了個兔?吐了還要吐?首先今天,我們自己用android程式實現一個兔年的新年賀卡。下面就是見證美好的時刻,上效果。
好,我們來使用Android動畫的知識,來實現這樣一個動畫效果吧。
架構設計、Android檢視動畫、TypeEvaluator、Path、組合模式、代理模式。
我們回顧動畫的種類,補間動畫、幀動畫、屬性動畫以及Android View自帶的檢視動畫。我們今天自己基於屬性動畫來打造一個山寨版的Android檢視動畫吧。我們可以從平移動畫、縮放動畫、旋轉動畫和透明度動畫中抽象出一個基礎類別Action類。我是不會告訴你這個類的命名我是抄的cocos2d的。然後我們擴充套件Action類,實現這四種動畫,再作用在View上。這樣就可以讓View按我們的動畫框架播放動畫了。
/** * 組合的action可以直接交給view執行。 */ interface Action<A : Action<A>> { fun add(action: A): A fun getAnimator(): Animator<A> fun startAnimation(view: View, duration: Long) }
抽象一個Action介面,Action還可以新增Action,這裡是組合模式的結構。
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中實現屬性動畫的邏輯,然後聚合到Action類的實現,通過代理的方式呼叫我們的動畫實現。這裡我們重寫了+號操作符,這樣可以支援兩個物件進行相加,這個是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放到一個List集合中儲存起來,當我們呼叫startAnimation()方法,則可以將傳入的View拿到,並執行動畫。
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的實現,我們這裡最關鍵的一行程式碼就是使用了ObjectAnimator,用它來監聽該物件屬性的變化。比如這裡我們監聽alpha屬性實際上是監聽的setAlpha方法。動畫變化的中間值則是通過TypeEvaluator估值器來進行計算估值的。在startAnimation()方法被呼叫的時候,我們預設在最前面新增了一個預設值。
actionTree.add(0, AlphaAction(1.0f))
我這裡只是拋磚引玉,你可以做得更好,比如將初始狀態不要寫死,讓子類去指定或在使用的時候動態指定,這樣就會更加的靈活。
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實現移動動畫。
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 } }
曲線運動則牽扯到一些貝瑟爾曲線的知識。 比如二階的貝瑟爾曲線
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)
直線運動則是定義了MoveTo和LineTo兩個類。
class MoveTo(x: Float, y: Float) : PathAction(x, y)
class LineTo(x: Float, y: Float) : PathAction(x, y)
我們賀卡的動畫就是使用了以下的寫法,同一類Action可以通過+號操作符進行合併,我們可以同時呼叫這四類Action進行動畫效果的疊加,這樣可以讓動畫效果更加豐富。
(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手機上實現各種各樣炫酷的效果。跟著哆啦一起玩轉Android自定義View吧。
以上就是Android程式碼實現新年賀卡動畫範例詳解的詳細內容,更多關於Android新年賀卡動畫的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45