美文网首页程序员技术栈AndroidAndroid之界面
炫酷!从未见过如此Q弹的Switcher

炫酷!从未见过如此Q弹的Switcher

作者: 依然范特稀西 | 来源:发表于2020-03-12 18:27 被阅读0次

    前言

    最近逛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

    掘金:https://juejin.im/user/56949a9960b2e058a42be0ba

    公众号.png

    相关文章

      网友评论

        本文标题:炫酷!从未见过如此Q弹的Switcher

        本文链接:https://www.haomeiwen.com/subject/xoundhtx.html