炫酷!從未見過如此Q彈的Switcher

前言

最近逛Dribbble的時候,看到了一個非常酷的Switcher動畫,特別喜歡,本想着試着用代碼在Android平臺來實現一下,沒想到已經有實現的版本,並且作者還寫了文章分享,思路清晰,各個實現關鍵點都講的特別清楚,因此就譯誠中文,分享大家,正如作者最後所說,大家一定要運行試試,效果非常贊!

原作者:Alexander Kolpakov
譯者:依然範特稀西
地址:http://suo.im/60UJjT

正文開始

最近,我寫了一篇關於實現Dribbble上的一個漂亮設計的文章。得到了很多積極的反饋,對我來說,這給了我很大的動力。我非常高興能獲得這些反饋,同時我也很樂意分享我的經驗。

在本文中,我們將嘗試逐步實現由Oleg Frolov創建的另一個精美的動畫。這與上一篇文章中的複雜動畫UI無關,它是一個自定義小控件。但是它有着非常精美漂亮的動畫設計,如下所示:

swicher.gif

乍一看,實現這樣的切換似乎並不簡單,但我認爲那是因爲動畫非常漂亮。如 你所見,創建相同的動畫並不難。讓我們一步一步地來實現它。

第一步,我們需要自定義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小部件。僅需將視圖輪廓從圓角矩形更新爲圓形,並禁用平移動畫。就是這麼簡單。

swicherX.gif

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

公衆號.png

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章