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

前言

最近逛Dribbble的时候,看到了一个很是酷的Switcher动画,特别喜欢,本想着试着用代码在Android平台来实现一下,没想到已经有实现的版本,而且做者还写了文章分享,思路清晰,各个实现关键点都讲的特别清楚,所以就译诚中文,分享你们,正如做者最后所说,你们必定要运行试试,效果很是赞!android

原做者:Alexander Kolpakov 译者:依然范特稀西 地址:http://suo.im/60UJjTgit

正文开始

最近,我写了一篇关于实现Dribbble上的一个漂亮设计的文章。获得了不少积极的反馈,对我来讲,这给了我很大的动力。我很是高兴能得到这些反馈,同时我也很乐意分享个人经验。github

在本文中,咱们将尝试逐步实现由Oleg Frolov建立的另外一个精美的动画。这与上一篇文章中的复杂动画UI无关,它是一个自定义小控件。可是它有着很是精美漂亮的动画设计,以下所示:web

swicher.gif
swicher.gif

乍一看,实现这样的切换彷佛并不简单,但我认为那是由于动画很是漂亮。如 你所见,建立相同的动画并不难。让咱们一步一步地来实现它。spring

第一步,咱们须要自定义View,而且实现它的3个构造方法:canvas

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扩展便可:app

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)方法,咱们须要三个动画:编辑器

  • 一、switcherAnimator: 切换器图标动画,从白色矩形到圆形,反之亦然ide

  • 二、translateAnimator: 为切换器图标从左到右的过渡设置动画,反之亦然;

  • 三、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来实现反弹效果,这很是适合咱们的动画场景,不了解的能够看一下这片文章:evgenii.com/blog/spring…,它解释了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,是由于咱们须要平滑的动画,而且第一个方法有优点,详情请看:stackoverflow.com/questions/2…

translateAnimator的工做方式相同,可是会更新Swicher图标的x位置。

如你所见,咱们的白色圆圈就像百吉饼。咱们有2种制做方法:

  • 裁剪一个较小的圆圈
  • 最简单的圆圈,只需在顶部绘制另外一个小圆圈,而后用切换器颜色填充便可。

我选择较简单的一种。

这一切,没什么难的!咱们如今有一个漂亮的自定义小控件而且带有精美的动画!

至此,咱们一切均可以了

如今,咱们可使用任何类型的动画来建立自定义视图,并且咱们能够稍微更改代码以建立另外一个由Oleg Frolov设计的Swicher小部件。仅需将视图轮廓从圆角矩形更新为圆形,并禁用平移动画。就是这么简单。

swicherX.gif
swicherX.gif

Talk is Chep,Just show Code

随意获取GitHub上的源代码,查看个人其余实现,别忘了尝试一下! Github: github.com/bitvale/Swi…

以上就是所有内容,感谢你的阅读,最后,别忘了点赞和收藏!

若是你喜欢个人文章,就关注下个人公众号 Android技术杂货铺 、 简书 或者Github! 微信公众号:Android技术杂货铺

简书:www.jianshu.com/u/35167a70a…

GitHub:github.com/pinguo-zhou…

相关文章
相关标签/搜索