preface

Recently, when I was browsing Dribbble, I saw a very cool Switcher animation, which I liked very much. I thought I would try to implement it in Android with code, but I didn’t realize that there is already an implementation version, and the author also wrote an article to share. The ideas are clear, and the key points of implementation are very clear. Share everyone, as the author finally said, we must run try, the effect is very good!

By Alexander Kolpakov. Translator: Still Fantasia Address: http://suo.im/60UJjT

The text start

Recently, I wrote an article about implementing a nice design on Dribbble. I got a lot of positive feedback and, for me, it gave me a lot of motivation. I’m very happy to get this feedback, and I’m happy to share my experience.

In this article, we’ll try to step through another beautiful animation created by Oleg Frolov. This has nothing to do with the complex animated UI from the previous article, but is a custom widget. But it has a very nice and beautiful animation design, like this:

swicher.gif

At first glance, it doesn’t seem easy to make such a switch, but I think that’s because the animation is so beautiful. As you can see, it’s not hard to create the same animation. Let’s do it step by step.

First, we need to customize the View and implement its three constructors:

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

}

}

Copy the code

Let’s go ahead and draw

By default (selected), our switch consists of two rounded rectangles (green and white)


Drawing them is very simple, we just need to calculate the position of the white rectangle and pass the offset iconTranslateX to the Android KTX Canvas.withTranslation extension:

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)

}

}

Copy the code

Start breaking down the animation

For the animation part, we use ValueAnimator, using its ofFloat(float… Values) method, we need three animations:

  • 1. SwitcherAnimator: Animator icon animation, from white rectangle to round, and vice versa

  • TranslateAnimator: Animates the switcher icon from left to right and vice versa;

  • 3, colorAnimator: Change the color from green (selected) to red and vice versa.

Let’s take a look at the switcherAnimator animation and set 0 to the start of the animation and 1 to the end of the animation.

// ...

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

}

// ...

Copy the code

BounceInterpolator we can use BounceInterpolator written by Evgenii Neumerzhitckii to implement rebound effects, which is very suitable for our animation scenes. For those who don’t know, check out this article: evgenii.com/blog/spring… It explains how BounceInterpolator works.

In the Android KTX addUpdateListener extension, we updated the value of progress, then triggered the invalidate method, and finally redrew the view.

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()

}

}

Copy the code

The LERP method is similar to a linear interpolator in that it is used to calculate iconOffset and, in turn, it is used to calculate the rounded rectangle coordinates of the Swicher icon. The rectangle of this icon changes from a rounded rectangle to a circle (a rounded rectangle with a larger rounded radius).

We use the postInvalidateOnAnimation () instead of postIvalidate, because we need a smooth animation, and the first method has advantages, see: stackoverflow.com/questions/2…

TranslateAnimator works the same way, but updates the X position of Swicher’s icon.


As you can see, our white circles are like bagels. We have 2 ways to make it:

  • Cut out a smaller circle
  • For the simplest circle, just draw another small circle at the top and fill it with the switcher color.

I’ll take the easier one.

All this, nothing hard! We now have a nice custom widget with nice animations!

At this point, we are all set

Now we can use any type of animation to create a custom view, and we can change the code slightly to create another Swicher widget designed by Oleg Frolov. Simply update the view contour from rounded rectangle to round and disable panning animation. It’s that simple.

swicherX.gif

Talk is Chep, Just show Code

Feel free to grab the source code on GitHub, check out my other implementations, and don’t forget to give it a try! Making: github.com/bitvale/Swi…

That’s all, thanks for reading, and don’t forget to like and bookmark!

If you like my article, please follow my public account Android technology grocery store, Jian Shu or Github! Wechat official account: Android technology Grocery Store

Jane: www.jianshu.com/u/35167a70a…

GitHub:github.com/pinguo-zhou…