前言
最近逛Dribbble
的時候,看到了一個非常酷的Switcher
動畫,特別喜歡,本想着試着用代碼在Android平臺來實現一下,沒想到已經有實現的版本,並且作者還寫了文章分享,思路清晰,各個實現關鍵點都講的特別清楚,因此就譯誠中文,分享大家,正如作者最後所說,大家一定要運行試試,效果非常贊!
原作者:Alexander Kolpakov
譯者:依然範特稀西
地址:http://suo.im/60UJjT
正文開始
最近,我寫了一篇關於實現Dribbble上的一個漂亮設計的文章。得到了很多積極的反饋,對我來說,這給了我很大的動力。我非常高興能獲得這些反饋,同時我也很樂意分享我的經驗。
在本文中,我們將嘗試逐步實現由Oleg Frolov
創建的另一個精美的動畫。這與上一篇文章中的複雜動畫UI無關,它是一個自定義小控件。但是它有着非常精美漂亮的動畫設計,如下所示:
乍一看,實現這樣的切換似乎並不簡單,但我認爲那是因爲動畫非常漂亮。如 你所見,創建相同的動畫並不難。讓我們一步一步地來實現它。
第一步,我們需要自定義View,並且實現它的3個構造方法:
class Switcher @JvmOverloads constructor(
context: Context,
attrs: AttributeSet ? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
init {
attrs?.let { retrieveAttributes(attrs, defStyleAttr) }
}
private fun retrieveAttributes(attrs: AttributeSet, defStyleAttr: Int) {
// retrieve cutom attributes
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// setup switcher width and height
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
// setup helper sizes every time switcher size changed (radius, icon width...)
}
override fun onDraw(canvas: Canvas ?) {
// just draw
}
}
接下來開始繪製
在默認(選中)狀態下,我們的開關由2個圓角矩形(綠色和白色)組成
繪製它們非常簡單,我們只需要計算白色矩形的位置並將偏移量iconTranslateX
傳遞給Android KTX Canvas.withTranslation
擴展即可:
override fun onDraw(canvas: Canvas ?) {
// draw switcher (green rect)
canvas?.drawRoundRect(switcherRect, switcherCornerRadius, switcherCornerRadius, switcherPaint)
// draw icon (white rect)
canvas?.withTranslation(x = iconTranslateX) {
drawRoundRect(iconRect, switcherCornerRadius, switcherCornerRadius, iconPaint)
}
}
開始分解動畫
動畫部分,我們使用ValueAnimator
來實現,使用它的ofFloat(float... values)
方法,我們需要三個動畫:
-
1、
switcherAnimator
: 切換器圖標動畫,從白色矩形到圓形,反之亦然 -
2、
translateAnimator
: 爲切換器圖標從左到右的過渡設置動畫,反之亦然; -
3、
colorAnimator
: 將顏色從綠色(選中)更改爲紅色,反之亦然。
讓我們先看一下switcherAnimator
動畫,設置0
爲動畫的開始值,1
爲動畫的結束值。
// ...
var amplitude = BOUNCE_ANIM_AMPLITUDE_IN
var frequency = BOUNCE_ANIM_FREQUENCY_IN
var newProgress = 1f
if (!checked) {
amplitude = BOUNCE_ANIM_AMPLITUDE_OUT
frequency = BOUNCE_ANIM_FREQUENCY_OUT
newProgress = 0f
}
val switcherAnimator = ValueAnimator.ofFloat(iconProgress, newProgress).apply {
addUpdateListener {
iconProgress = it.animatedValue as Float
}
interpolator = BounceInterpolator(amplitude, frequency)
duration = SWITCHER_ANIMATION_DURATION
}
// ...
我們可以使用 Evgenii Neumerzhitckii 寫的BounceInterpolator
來實現反彈效果,這非常適合我們的動畫場景,不瞭解的可以看一下這片文章:https://evgenii.com/blog/spring-button-animation-on-android/,它解釋了BounceInterpolator
是如何工作的。
在Android KTX addUpdateListener
擴展中,我們更新了progress
的值,然後觸發invalidate
方法,最後重新繪製了視圖。
private var iconProgress = 0f
set(value) {
if (field != value) {
field = value
val iconOffset = lerp(0f, iconRadius - iconCollapsedWidth / 2, value)
iconRect.left = width - switcherCornerRadius - iconCollapsedWidth / 2 - iconOffset
iconRect.right = width - switcherCornerRadius + iconCollapsedWidth / 2 + iconOffset
postInvalidateOnAnimation()
}
}
lerp
方法類似一個線性插值器,它用於計算iconOffset
,反過來,它也用於計算Swicher
圖標的圓角矩形座標。此圖標的矩形從一個圓角矩形變爲一個圓形(圓角半徑較大的圓角矩形)。
我們使用了
postInvalidateOnAnimation()
代替postIvalidate
,是因爲我們需要平滑的動畫,並且第一個方法有優勢,詳情請看:https://stackoverflow.com/questions/29219372/postinvalidateonanimation-vs-postinvalidate/42648958#42648958
translateAnimator
的工作方式相同,但是會更新Swicher
圖標的x
位置。
如你所見,我們的白色圓圈就像百吉餅。我們有2種製作方法:
- 裁剪一個較小的圓圈
- 最簡單的圓圈,只需在頂部繪製另一個小圓圈,然後用切換器顏色填充即可。
我選擇較簡單的一種。
這一切,沒什麼難的!我們現在有一個漂亮的自定義小控件並且帶有精美的動畫!
至此,我們一切都可以了
現在,我們可以使用任何類型的動畫來創建自定義視圖,而且我們可以稍微更改代碼以創建另一個由Oleg Frolov
設計的Swicher
小部件。僅需將視圖輪廓從圓角矩形更新爲圓形,並禁用平移動畫。就是這麼簡單。
Talk is Chep,Just show Code
隨意獲取GitHub上的源代碼,查看我的其他實現,別忘了嘗試一下!
Github: https://github.com/bitvale/Switcher
以上就是全部內容,感謝你的閱讀,最後,別忘了點贊和收藏!
如果你喜歡我的文章,就關注下我的公衆號 Android技術雜貨鋪 、 簡書 或者Github!
微信公衆號:Android技術雜貨鋪簡書:https://www.jianshu.com/u/35167a70aa39
GitHub:https://github.com/pinguo-zhouwei