From design to android, part 2

It’s been a long time since my previous post, many things have happened since then, but finally is here. I hope you enjoy it!.

This is a new story in my series of articles called ‘From design to android’, if you remember the first part of these series, I pick a design concept which I find interesting and I try to implement it on Android, focusing on some interesting topics along the the way from my android developer perspective.

All the code referred in this post can be found at this Github’s repository 👈

This is the concept that I’ve chosen for this part:

Google business — Slider by Johny Vino

I used once again a concept coming from Johny Vino.

Try to stop for a second figuring out, as an android developer as you are, how to make an implementation of that screen.

If you spotted two of the following points you maybe agreed with me, at first sight, there were two parts that came to my mind:

  • How to implement the animation of the building, of course.
  • That seekbar might not be as trivial as it should be

Animating the building

Let’s put this exercise on a perfect environment, we may ask our designer(s) to create the animation. After maybe, some After Effects work + the Bodymovin’s plugin, they could give us the desired animated vector drawable.

Or…, we might have no designer at all, and you are a brave and lonely developer which needs to have this done without much design skills, let’s try that path.

Working with vectors

With some minor work on a vector based tool like Sketch or BoxySvg we could easily vectorise the building image. Grouping those parts whose will be animated into groups will be useful for a next point.

Creating the desired image on Sketch

Next, a new amazing tool comes to the scene, and this one is Shape Shifter, from Alex Lockwood. This awesome tool helps you to animate SVG based images and exporting those animations into an AVD among other formats.

Shape Shifter

After creating the desired effect in a few clicks, we can export our fresh animated vector drawable, paste it into Android Studio, bind it to an ImageView and we are done!.

(img_building.drawable as Animatable).start()
Exported ShapeFilter’s avd running on an android device

Yeah, amazing, right?! We just built a great animation effortlessly, but, if we look again at Johny Vino’s concept, we’ve missed minor detail. The animation is handled by a slide bar, (known as Seekbar on Android) which handles the sate of the animation depending the progress of the bar.

So what we need is so far to set the progress of the animation within an AVD. Well… that’s not currently supported from what I’ve seen, (Lottie does). We just have at our disposal methods like, .start().stop() and reset().

At this point we could try the following:

Or keep going, let’s iterate just a bit more.


Animated Selector Drawables

In our current point we have an animation which needs to be commanded to go to an specific frame when the SeekBar reachs an specific position. In other words we have some states during the animation.

Let’s declare some attributes for defining these frames or states.

<declare-styleable name="BuildingState">
<!-- Idle state -->
<attr name="state_zero" format="boolean"/>
<!-- One flat and a simple roof -->
<attr name="state_one" format="boolean"/>
<!-- Two flats and expanded roof -->
<attr name="state_two" format="boolean"/>
</declare-styleable>

In this point let me introduce a maybe not well known drawable, the AnimatedStateListDrawable which totally will save our day.

Let’s refer to the android documentation:

Drawable containing a set of Drawable keyframes where the currently displayed keyframe is chosen based on the current state set. Animations between keyframes may optionally be defined using transition elements.
This drawable can be defined in an XML file with the <animated-selector> element. Each keyframe Drawable is defined in a nested <item> element. Transitions are defined in a nested <transition> element.

Looks somewhat what we are looking for, right? We could define some <items> which will identify the different frames in our animation, and transitions for how transition an item to another.

Since every <item> tag would represent our frame, we’ll define three of them, each of one composed by a vector drawable.

drawable/vd_building1.xml
drawable/vd_building2.xml
drawable/vd_building3.xml

And each<transition> will represent how an <item> transitions to another, we’ll need 4 for transitioning through all the frames back and forth.

With the aforementioned Shape Shifter, we can easily export the required AVDs into Android Studio, which will work as transitions in the AnimationStateListDrawable.

drawable/avd_building_1_2
drawable/avd_building_2_3
drawable/avd_building_3_2
drawable/avd_building_2_1
avd_building_1_2.xml
avd_building_2_3.xml

And finally the whole AnimatedStateDrawable we just built:

<?xml version="1.0" encoding="utf-8"?>
<animated-selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>

<item
android:id="@+id/frame_0"
android:drawable="@drawable/vd_building_1"
app:state_one="false"
app:state_two="false"
/>

<item
android:id="@+id/frame_1"
android:drawable="@drawable/vd_building_2"
app:state_one="true"
/>

<item
android:id="@+id/frame_2"
android:drawable="@drawable/vd_building_3"
app:state_two="true"
/>

<transition
android:fromId="@id/frame_0"
android:toId="@id/frame_1"
android:drawable="@drawable/avd_building_1_2"
/>

<transition
android:fromId="@id/frame_1"
android:toId="@id/frame_2"
android:drawable="@drawable/avd_building_2_3"
/>

<transition
android:fromId="@id/frame_2"
android:toId="@id/frame_1"
android:drawable="@drawable/avd_building_3_2"
/>

<transition
android:fromId="@id/frame_1"
android:toId="@id/frame_0"
android:drawable="@drawable/avd_building_2_1"
/>

</animated-selector>

Tweaking a bit our activity and our layout we set this amazing AnimatedSelectorDrawable into an ImageView as the source resource, and making use of the setImageState method with the desired state, the animation will work magically as expected.

private val STATE_ZERO = intArrayOf(
R.attr.state_zero, -R.attr.state_one, -R.attr.state_two
)

private val STATE_ONE = intArrayOf(
-R.attr.state_zero, R.attr.state_one, -R.attr.state_two
)

private val STATE_TWO = intArrayOf(
-R.attr.state_zero, -R.attr.state_one, R.attr.state_two
)
private fun onSeekProgressChanged(position: Int) {
val max = seekbar.max

val businessType = when(position) {
in 0..max/3 -> STATE_ZERO
in
10..max/2 -> STATE_ONE
in
20..max/1 -> STATE_TWO
else
-> throw IllegalStateException()
}

imageView.setImageState(businessType, true)
}

Result:


Animating Seekbar’s thumb

Yai! So far, we’ve reached one of my two hot points, the second one is manage how to animate the Seekbar thumb’s size during the progress, as Johny Vino does in his concept:

Size of the Seekbar’s thumb on dragging

We can distinguish two behaviours here:

  • The thumb size is being increased in some way related with the progress
  • When releasing, some kind overshooting animation its being performed

A visit to the ScaleDrawable

Other unknown drawable (at least for me) is the ScaleDrawable, let’s refer again the android documentation:

A Drawable that changes the size of another Drawable based on its current level value. You can control how much the child Drawable changes in width and height based on the level, as well as a gravity to control where it is placed in its overall container. Most often used to implement things like progress bars.
The default level may be specified from XML using the android:level property. When this property is not specified, the default level is 0, which corresponds to zero height and/or width depending on the values specified for android.R.styleable#ScaleDrawable_scaleWidth scaleWidthand android.R.styleable#ScaleDrawable_scaleHeight scaleHeight. At run time, the level may be set via setLevel(int).

So, it seems that we could define some kind of drawable which will refers another drawable, a level, and a scale factor. If we attach the progress of the bar to that level, it should work, right?.

Let’s define a layer list of drawables and, since we only want the blue part to be scaled, we’ll wrap it into an ScaleDrawable.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
>
<item>
<scale
android:scaleWidth="30%"
android:scaleHeight="30%"
android:scaleGravity="center"
android:level="1"
tools:ignore="UnusedAttribute"
>

<aapt:attr name="android:drawable">
<shape android:shape="oval">
<size
android:width="70dp"
android:height="70dp"
/>

<solid android:color="@color/blue_accent_200" />
</shape>
</aapt:attr>
</scale>
</item>
<item
android:left="25dp"
android:top="25dp"
android:right="25dp"
android:bottom="25dp"
>
<shape android:shape="oval">
<solid android:color="#FFF" />
</shape>
</item>
</layer-list>

As you may see, using the <aapt> tool we can inline required attributes right in the <layer-list>

thumb <layer-list> containing a ScaleDrawable

Now we just only have to tweak again our activity to set the level of the ScaleDrawable, right when the progress of the SeekBar changes, notice also what nice is it that we can use the level property from the Drawable of the thumb.

private fun onSeekProgressChanged(position: Int) {
// ...

((seekbar.thumb as LayerDrawable)).level =
(position * (SCALE_MAX / seekbar.max))
}

And the result:

Now we just have to animate the thumb when releasing, we can get rid of that making use of the ScaleDrawable and a ValueAnimator.

private fun animateThumbRelease() {
val thumb = seekbar.thumb
val initLevel = thumb.level
val maxLevel = thumb.level * THUMB_RELEASE_SCALE_FACTOR
val animator = ValueAnimator.ofInt(
initLevel, maxLevel, initLevel)

with(animator) {
interpolator = OVERSHOOT
duration = THUMB_SCALE_DURATION

addUpdateListener {
thumb.level = it.animatedValue as Int
}

start()
}
}

Result:


Wrapping up

At this point we’ve solved the issue with the animation, and after that the one with the Seekbar, with a few more trivial modifications as creating an AVD for the moving clouds, using the new font as resources capability for our texts, and adding a fresh new adaptive icon, we have something quite similar to the Johny Vino’s concept.

I hope you enjoyed during the road and, if you have to kill me because something, please do it with a nice comment and an aggressive emoji.

Final result:


Github Repository

here 👈

References