Yet Another [à compléter]

Custom Interactions With the Design Support Library

During this year Google I/O, a new entry in the support library family was added in the form of support-design.

The goal of this new library is to brige the gap between the theory of the Material Design specification and having actual available code to implement it.

The new library sits on top of the existing support-v7-appcompat package (which provides the base Material theming ability) and offers a bunch of new UI components and interactions from the specification.

Today I want to focus on two of those new elements that we will be using together: the infamous FAB (Floating Action Button) and CoordinatorLayout.

The FAB strikes back

If I had to pick one of the most iconic aspect of material design it would be the floating action button (henceforth known as FAB). It’s actually deemed important enough that the specification was expanded recently to have a dedicated section for it.

FAB are circular buttons containing a single icon (usually white or black) with a bright background (the color accent of your app for instance) and generally elevated above content (thus casting a shadow).

These FABs showcase the main actions that your app has to offer. As such, they are ones of the most important part of your UI and you should always pay special attention to get their interactions right.

Because FABs are so proeminent visually, they also offer a good opportunity to add pleasant touches to your app. In the word of the specification: “Take advantage of [FAB’s] visibility to create delightful transitions for a primary UI element.”

The support design library offers a default implementation of that UI widget that’s backward compatible with older versions of Android that don’t have elevation/shadow support or round clipping in the form of the FloatingActionButton widget which is a subclass of the classic ImageView.

Here I want to focus on one of the documented FAB transition: morphing. This transition is used to alternate between two main actions for a single FAB depending on the context:

To implement this, you can create a simple subclass of the default FAB that let you also specify a secondary set of icon and background and then create a custom animation to alternate between the two:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class SwitchableFab : FloatingActionButton
{
	AnimatorSet switchAnimation;
	bool state;
	Drawable srcFirst, srcSecond;
	ColorStateList backgroundTintFirst, backgroundTintSecond;

	// ctors.

	void Initialize (Context context, IAttributeSet attrs)
	{
		srcFirst = this.Drawable;
		backgroundTintFirst = this.BackgroundTintList;

		if (attrs == null)
			return;
		var array = context.ObtainStyledAttributes (attrs, Resource.Styleable.SwitchableFab);
		srcSecond = array.GetDrawable (Resource.Styleable.SwitchableFab_srcSecond);
		backgroundTintSecond = array.GetColorStateList (Resource.Styleable.SwitchableFab_backgroundTintSecond);
		array.Recycle ();
	}

	public void Switch ()
	{
		if (state)
			Switch (srcFirst, backgroundTintFirst);
		else
			Switch (srcSecond, backgroundTintSecond);
		state = !state;
	}

	void Switch (Drawable src, ColorStateList tint)
	{
		const int ScaleDuration = 200;
		const int AlphaDuration = 150;
		const int AlphaInDelay = 50;
		const int InitialDelay = 100;

		if (switchAnimation != null) {
			switchAnimation.Cancel ();
			switchAnimation = null;
		}

		var currentSrc = this.Drawable;

		// Scaling down animation
		var circleAnimOutX = ObjectAnimator.OfFloat (this, "scaleX", 1, 0.1f);
		var circleAnimOutY = ObjectAnimator.OfFloat (this, "scaleY", 1, 0.1f);
		circleAnimOutX.SetDuration (ScaleDuration);
		circleAnimOutY.SetDuration (ScaleDuration);

		// Alpha out of the icon
		var iconAnimOut = ObjectAnimator.OfInt (currentSrc, "alpha", 255, 0);
		iconAnimOut.SetDuration (AlphaDuration);

		var outSet = new AnimatorSet ();
		outSet.PlayTogether (circleAnimOutX, circleAnimOutY, iconAnimOut);
		outSet.SetInterpolator (AnimationUtils.LoadInterpolator (Context,
		                                                         Android.Resource.Animation.AccelerateInterpolator));
		outSet.StartDelay = InitialDelay;
		outSet.AnimationEnd += (sender, e) => {
			BackgroundTintList = tint;
			SetImageDrawable (src);
			JumpDrawablesToCurrentState ();
			((Animator)sender).RemoveAllListeners ();
		};

		// Scaling up animation
		var circleAnimInX = ObjectAnimator.OfFloat (this, "scaleX", 0.1f, 1);
		var circleAnimInY = ObjectAnimator.OfFloat (this, "scaleY", 0.1f, 1);
		circleAnimInX.SetDuration (ScaleDuration);
		circleAnimInY.SetDuration (ScaleDuration);

		// Alpha in of the icon
		src.Alpha = 0;
		var iconAnimIn = ObjectAnimator.OfInt (src, "alpha", 0, 255);
		iconAnimIn.SetDuration (AlphaDuration);
		iconAnimIn.StartDelay = AlphaInDelay;

		var inSet = new AnimatorSet ();
		inSet.PlayTogether (circleAnimInX, circleAnimInY, iconAnimIn);
		inSet.SetInterpolator (AnimationUtils.LoadInterpolator (Context,
		                                                        Android.Resource.Animation.DecelerateInterpolator));

		switchAnimation = new AnimatorSet ();
		switchAnimation.PlaySequentially (outSet, inSet);
		switchAnimation.Start ();
	}
}

You can then instantiate this new class directly in your XML layout:

1
2
3
4
5
6
7
8
9
10
<myApp.SwitchableFab
    android:id="@+id/fabButton"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="end|bottom"
    android:layout_marginRight="16dp"
    android:layout_marginBottom="16dp"
    android:src="@drawable/ic_action_mylocation"
    app:srcSecond="@drawable/ic_favorite_selector"
    app:backgroundTintSecond="@color/favorite_background" />

For that interaction, the icon itself is normally not supposed to scale but since the widget is based on ImageView it’s impractical right now to separate the two layers to animate a counter-scale for the icon.

I imagine that a default implementation of the documented FAB interactions will likely find its way into the library at some stage but in the meantime it’s very easy to cook them up yourself like above.

You may also have noticed in the video that the second state of my FAB uses an animated selector. This is done by augmenting the SwitchableFab class to track the checkable state (as I have already demonstrated).

In any case, you can find the full implementation of that selector and checkable changes in Moyeu repository.

CoordinatorLayout, the missing orchestra conductor

Another interesting type that support-design brings is CoordinatorLayout.

On the surface it doesn’t seem to do much, it’s intended as a wrapper around your existing UI that behaves like a FrameLayout from a layout perspective. The true value of CoordinatorLayout is found on its child views.

Just like its name imply, CoordinatorLayout serves as a central conductor for more complex transitions that your app UI may be doing especially when involving several floating views like the aformentioned FAB or snackbars.

The core working of the class relies on so-called Behaviors that can be set on any child views of the CoordinatorLayout. In a behavior implementation, views can define if they want to be dependent on some other views and get a callback when that other view state (position, size, etc…) changes.

Behaviors can be attached to views in a multitude of ways but here is how you can declare it directly in your layout XML:

1
2
3
<!-- Based on the same FAB definition than above -->
<moyeu.SwitchableFab
    app:layout_behavior="md55d31ab91effba0f9ed7ec79c59c38391.InfoPaneFabBehavior" />

Here I made a custom behavior that allows me to automatically track the state of my application bottom pane as it appears/disappears and is dragged by the user so that the FAB automatically stick to it and change its state when the pane is initially expanded (using the transition animation we saw earlier):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class InfoPaneFabBehavior : CoordinatorLayout.Behavior
{
	int minMarginBottom;
	bool wasOpened = false;

	public InfoPaneFabBehavior (Context context, IAttributeSet attrs) : base (context, attrs)
	{
		minMarginBottom = (int)TypedValue.ApplyDimension (ComplexUnitType.Dip, 16, context.Resources.DisplayMetrics);
	}

	public override bool LayoutDependsOn (CoordinatorLayout parent, Java.Lang.Object child, View dependency)
	{
		return dependency is InfoPane;
	}

	public override bool OnDependentViewChanged (CoordinatorLayout parent, Java.Lang.Object child, View dependency)
	{
		// Move the fab vertically to place correctly wrt the info pane
		var fab = child.JavaCast<SwitchableFab> ();
		var currentInfoPaneY = ViewCompat.GetTranslationY (dependency);
		var newTransY = (int)Math.Max (0, dependency.Height - currentInfoPaneY - minMarginBottom - fab.Height / 2);
		ViewCompat.SetTranslationY (fab, -newTransY);

		// If alternating between open/closed state, change the FAB face
		if (wasOpened ^ ((InfoPane)dependency).Opened) {
			fab.Switch ();
			wasOpened = !wasOpened;
		}

		return true;
	}
}

Dead simple right?

CoordinatorLayout can also be used to implement other user interactions like swipe-to-dismiss or other scrolling techniques (like collapsing toolbars). Checkout the provided SwipeDismissBehavior and AppBarLayout.Behavior for more information on those scenarios.

Advanced Transitions With Vector Drawable

As I described in a previous serie of post, vector drawables (and their animated counterparts) are one of the most powerful new visual elements introduced with Lollipop (and soon as a support lib it seems).

In this post, I’m gonna show you how we can achieve the following transition animation by solely using vector drawables and Android’s built-in widget state tracking mechanism:

Staging

To build an animation like this, you have to prepare the adequate stage. Afterwards, it’s just a matter of moving each individual pieces of that stage when needed.

In the above video, we can split the final visual in a couple of different parts.

Some like the center dot and the upper-half donut are static pieces, they never move. The two white thin rectangles, symbolizing the hands on a watch, are individual moving parts. The sun, moon and exclamation mark symbols are the varying state of the visual depending on the input.

Now, that’s only the visible part of the puzzle. If you look closely at the animation you can notice that everytime there is a state transition the right hand seems to be erasing the visible time symbol before the new one fade in.

This is where things get interesting because to be able to achieve this effect you have to get creative with the Z-ordering of your stage.

To understand this better, below is a rendering of all the layers of the visual:

The rectangle you see covering the half-bottom part of the picture is not an artifact. It’s painted with the same color than the overall window background thus acting as a cheap but effective hiding mask.

If I conceal this rectangle, here is what’s lurking behind: the other half of the visible top donut

To create the aformentioned effect, we will be moving that half-bottom donut to cover the top one, synchronizing it with the right clock hand. When not in use, it will be safely tucked behind our hiding rectangle.

Because vector drawables have a simple bottom to top drawing hierarchy, for this to work correctly we have declare pieces in a specific order:

  • top half donut
  • the 3 time symbols
  • bottom half donut
  • hiding rectangle
  • the 2 hands white rectangle

Based on our animation needs, we then split this further into actual vector drawable <group /> elements to arrive at the following shape (path data removed for brevity, you can download all the XML pack at the end of the article):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<?xml version="1.0" encoding="UTF-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="385dp"
	android:width="416dp"
	android:viewportHeight="385"
	android:viewportWidth="416">
	<group android:name="background">
		<path
			android:name="topPie"
			android:fillColor="#454D58"
			android:pathData="..." />
	</group>
	<group android:name="timeItems">
		<path
			android:name="moon"
			android:fillColor="#DAE2ED"
			android:fillAlpha="0"
			android:pathData="..." />
		<path
			android:name="sun"
			android:fillColor="#E39300"
			android:fillAlpha="0"
			android:pathData="..." />
		<path
			android:name="now"
			android:fillColor="#C52E26"
			android:fillAlpha="1"
			android:pathData="..." />
	</group>
	<group
		android:name="hidingPie"
		android:pivotX="208"
		android:pivotY="192.5">
		<path
			android:name="hidingPiePath"
			android:fillColor="#454D58"
			android:pathData="..." />
	</group>
	<group android:name="coverBackground">
		<path
			android:name="blueCover"
			android:fillColor="#343A42"
			android:pathData="..." />
		<path
			android:name="middleRing"
			android:fillColor="#D8D8D8"
			android:pathData="..." />
	</group>
	<group
		android:name="leftHand"
		android:pivotX="208"
		android:pivotY="192.5">
		<path
			android:fillColor="#D8D8D8"
			android:pathData="..." />
	</group>
	<group
		android:name="rightHand"
		android:pivotX="208"
		android:pivotY="192.5">
		<path
			android:fillColor="#D8D8D8"
			android:pathData="..." />
	</group>
</vector>

State tracking widget

Before we dwelve into some more animation tricks, we first need to investigate how to make widget to represent our triple state because there is nothing in the default framework that allow this.

The good news is that, as I showed in a previous blog, it’s very easy to create a custom, state-aware widget which is what we are going to do with this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class TriStateView : View
{
	int selectedIndex;

	public int SelectedIndex {
		get {
			return selectedIndex;
		}
		set {
			if (value < 0 || value > 2)
				throw new ArgumentOutOfRangeException ("value");
			JumpDrawablesToCurrentState ();
			selectedIndex = value;
			RefreshDrawableState ();
			Invalidate ();
		}
	}

	protected override int[] OnCreateDrawableState (int extraSpace)
	{
		switch (selectedIndex) {
		case 0:
			return new int[0];
		case 1:
			return new int[] { Android.Resource.Attribute.StateFirst };
		case 2:
			return new int[] { Android.Resource.Attribute.StateLast };
		default:
			return base.OnCreateDrawableState (extraSpace);
		}
	}
}

By setting our state transition drawable as the background of this view, it will automatically react to state changes and animate accordingly. Using the normal state tracking mechanism also means that interruption mid-animations are gracefully handled for us.

One more trick

At this stage, I won’t go over every details of the individual animation definitions, they all tweak standard things like alpha and rotation on each piece of our stage. Nonetheless, you can read my animated vector drawable entry for more details on how they work.

I will only show a little trick that is useful when setting up more complex stages like this: zero-duration animators.

Take the following example that animates the bottom-half donut (to create the progressive hiding effect):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8" ?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
	android:ordering="sequentially">
	<objectAnimator
		android:duration="850"
		android:propertyName="rotation"
		android:valueFrom="0"
		android:valueTo="180"
		android:valueType="floatType"
		android:interpolator="@android:interpolator/decelerate_quad" />
	<objectAnimator
		android:duration="0"
		android:propertyName="rotation"
		android:valueFrom="-180"
		android:valueTo="0"
		android:valueType="floatType" />
</set>

The idea is that right after we have covered the entire upper-half, we want to immediately tuck it back under our hiding rectangle. This is because we then fade-in the new time symbol which would be hidden too otherwise.

Using zero-duration animators allow you to do just that by applying direct values to animated properties. The fact that there is a from attribute doesn’t matter much but since those properties are write only it will prevent object animator from complaining about missing accessors.

I use this in a couple of places to reset the stage state (since vector drawable instances are reused across transitions), quickly move away elements or hide them from view when they are not useful anymore.

Source

You can download the XML definitions and widget source here.

Bug alert: there is currently a parsing bug where the animated vector drawable inflater will greedily read the entire document given to it. This causes an issue if you declare them inline in your animated state transition XML file because it will corrupt your entire state by reading everything instead of stopping at its corresponding end tag. The workaround is to always declare them in their own XML files like I did.

Android Material Image Loading

Among the plethora of seemingly impossible things in the Material Design specification, there is one that piqued my curiosity this morning.

It’s something everybody does at least once, if not everywhere, in his app which is loading and transitioning images.

Now to make this more glamour we generally all went with the classical, battle-tested approach of shifting the opacity of our image container to announce the change (that was even one of my first Android tip).

But the new approach taken by Material and detailed in the “Loading Images” section goes a lot further than this by also throwing some image levels manipulation in the mix.

The process is summarized in the following graph:

It outlines a 3-steps process where a combination of opacity, contrast/luminosity and saturation is used in concert to help salvage our poor users eyesight.

Android has always supported image manipulation through the ColorFilter class that can be set on any drawable and on some view classes (ImageView for instance).

When used with its 4x5 ColorMatrix-based implementation ColorMatrixColorFilter, you can virtually implement any kind of image transformation provided you grok the way vector/matrix multiplication work (head to the ColorMatrix documentation for the resulting equations).

The only thing that was a limiting factor is that a filter is initialized once and for all. If you want to change the effect you need to create a new instance of the filter (which initialize a native counterpart) and replace the old version with the new one.

Obviously this is a complete downer when you do animations because you don’t want to stress out the GC and GPU during those phases by creating a new instance of the filter at every refresh step.

But thanks to Lollipop and the fact that @hide can’t stop us, there is actually a new public method allowing us to update a filter after the fact.

Armed with this knowledge, we can now set out to create a custom ITypeEvaluator to tweak our filter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Refer to http://developer.android.com/reference/android/graphics/ColorMatrix.html
// for a list of the matrix indexes
class AlphaSatColorMatrixEvaluator : Java.Lang.Object, ITypeEvaluator
{
	ColorMatrix colorMatrix = new ColorMatrix ();
	float[] elements = new float[20];

	public ColorMatrix ColorMatrix {
		get { return colorMatrix; }
	}

	public Java.Lang.Object Evaluate (float fraction, Java.Lang.Object startValue, Java.Lang.Object endValue)
	{
		// There are 3 phases so we multiply fraction by that amount
		var phase = fraction * 3;

		// Compute the alpha change over period [0, 2]
		var alpha = Math.Min (phase, 2f) / 2f;
		elements [19] = (float)Math.Round (alpha * 255);

		// We substract to make the picture look darker, it will automatically clamp
		// This is spread over period [0, 2.5]
		const int MaxBlacker = 100;
		var blackening = (float)Math.Round ((1 - Math.Min (phase, 2.5f) / 2.5f) * MaxBlacker);
		elements [4] = elements [9] = elements [14] = -blackening;

		// Finally we desaturate over [0, 3], taken from ColorMatrix.SetSaturation
		float invSat = 1 - Math.Max (0.2f, fraction);
		float R = 0.213f * invSat;
		float G = 0.715f * invSat;
		float B = 0.072f * invSat;

		elements[0] = R + fraction; elements[1] = G;            elements[2] = B;
		elements[5] = R;            elements[6] = G + fraction; elements[7] = B;
		elements[10] = R;           elements[11] = G;           elements[12] = B + fraction;

		colorMatrix.Set (elements);
		return colorMatrix;
	}
}

Here is how you can set it up:

1
2
3
4
5
6
7
8
9
10
11
12
var imageView = FindViewById<ImageView> (Resource.Id.image);
var drawable = (BitmapDrawable)Resources.GetDrawable (Resource.Drawable.monkey);
var evaluator = new AlphaSatColorMatrixEvaluator ();
var filter = new ColorMatrixColorFilter (evaluator.ColorMatrix);
drawable.SetColorFilter (filter);

var animator = ObjectAnimator.OfObject (filter, "colorMatrix", evaluator,
                                        evaluator.ColorMatrix,
                                        evaluator.ColorMatrix);
animator.Update += (sender, e) => drawable.SetColorFilter (filter);
animator.SetDuration (2500);
animator.Start ();

And here is the result:

It’s probably safe to assume Google will come out with an official way for this pattern e.g. in a subsequent support library update. In the meantime, you can get cracking with this version.

Transitioning to Infinity

I remember clearly when I sat down at I/O this year, watching the introduction of Material design in the keynote unfold and how, at the time, I told myself there was no way we could implement all of this in Android.

One such aspect that I was highly skeptical of until very recently was the concept of animated transition.

We actually talked about this during our Material Design Evolve session with James and at the time of that talk, the only facility we had been given to do it was by keyframe animations which is incredibly clunky to maintain.

Meet our new private implementation friend, PathDataEvaluator.

This evaluator is able to understand the path definition used by vector drawable and create intermediary versions of it.

This means that given two specific path of a vector drawable, we can use an object animator to, not only animate transformation or style like we have seen before, but also the actual pathData of the vector itself.

Now before you get too excited, it’s not a miracle evaluator. There are two very strong requirements for it to work properly:

  • The path command list needs to be of the same size
  • Each command in that list needs to be of the same type

Basically, the evaluator treats the path data as an array of float extracted from each command parameters and use that to interpolate inbetweeners.

This essentially mean that when crafting your paths, you may need to get a bit creative in the amount of nodes you use in order to make those requirements fit.

As an example, this is what we are going to make:

Intuitively, based on our previously mentioned requirements, we can see there is already fundamental problem when we think of alternating between a standard play icon and a standard pause icon: they don’t have the same amounts of subparts (2 rectangles for pause, 1 triangle for play).

In addition, the pause icon rectangles necessitate 4 control points for each subpart whereas the triangle of the play icon only requires 3.

That’s where the tweaking part comes in, following are how I created those shapes in my editor to solve both issues (nodes handles are highlighted):

The work was actually focused on the play icon by first separating the triangle shape in two right sub-triangles and then adding a (normally unnecessary) extra node onto its hypotenuse.

Each node is also numbered as the order in which the path is drawn has an impact on the look and feel of the animation. With those numbers, you can visualize in your head how the nodes are going to move across the canvas to alternate between the two shapes.

Without further ado, here are the vector drawables we are using:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!-- In your strings.xml file -->
<resources>
	<string name="pause_vector_path">M12,10L20,10L20,38L12,38ZM28,10L36,10L36,38L28,38Z</string>
	<string name="play_vector_path">M16,24L38,24L27.3,30.8L16,38ZM16,10L27.3,17.2L38,24L16,24Z</string>
</resources>

<!-- Resources\drawable\ic_play.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="48dp"
	android:width="48dp"
	android:viewportHeight="48"
	android:viewportWidth="48">
	<group>
		<path android:name="d"
			android:fillColor="@color/white_primary"
			android:pathData="@string/play_vector_path" />
	</group>
</vector>

<!-- Resources\drawable\ic_play.xml is identical except
     replace pathData with the second string resource -->

The path data themselves are stored as a string resources. This makes it easier to use them in different places without having to copy and paste the entire thing.

Notice also how my commands are ordered (M stands for moveto and L for lineto) to reflect what the editor diagram showed and also how each command type maps to the same command type between the two definitions.

Since we want to create a transition we need a container for this that handle state using the Android standard state tracking mechanism. This type of container is called a state-list and, as we already covered in the Evolve video above, we now also have an equivalent version that supports animated transition between state: AnimatedStateListDrawable.

Here is my definition for the play/pause button state (based on the state_checked presence):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android"
    android:constantSize="true">
    <item
        android:drawable="@drawable/ic_pause"
        android:state_checked="true"
        android:id="@+id/pause_state" />
    <item
        android:drawable="@drawable/ic_play"
        android:id="@+id/play_state" />
	<transition android:fromId="@id/play_state" android:toId="@id/pause_state" android:reversible="true">
		<animated-vector android:drawable="@drawable/ic_play">
			<target android:name="d" android:animation="@anim/play_pause" />
		</animated-vector>
	</transition>
</animated-selector>

You can watch the Evolve video again for a longer explaination of how this works but essentially we are now naming our various <item /> elements with an id so that we can reference them for animation in <transition /> blocks.

Inside the transition, you’ll find our known companion AnimatedVectorDrawable referencing this object animator definition:

1
2
3
4
5
6
7
<!-- Resource\anim\play_pause.xml -->
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
	android:duration="250"
	android:propertyName="pathData"
	android:valueFrom="@string/play_vector_path"
	android:valueTo="@string/pause_vector_path"
	android:valueType="pathType" />

Key aspect is the valueType attribute that is set to pathType (the only actual way to can reference the non-public PathDataEvaluator class) and the property name set to pathData to change the corresponding member in the vector drawable. For the rest, you can tweak this like any other animator.

Last piece is to show this transition in something. The default Android framework offers a CheckedTextView for those cases but no CheckedImageButton (because using Checkbox can in some occasions result int a bit of pain with positioning). It’s easy to implement though:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class CheckedImageButton : ImageButton, ICheckable
{
	static readonly int[] CheckedStateSet = {
		Android.Resource.Attribute.StateChecked
	};
	bool chked;

	// Add standard constructors

	public override bool PerformClick ()
	{
		Toggle ();
		return base.PerformClick ();
	}

	public void Toggle ()
	{
		Checked = !Checked;
	}

	public bool Checked {
		get {
			return chked;
		}
		set {
			chked = value;
			RefreshDrawableState ();
			Invalidate ();
		}
	}

	public override int[] OnCreateDrawableState (int extraSpace)
	{
		var drawableState = base.OnCreateDrawableState (extraSpace + 1);
		if (Checked)
			MergeDrawableStates (drawableState, CheckedStateSet);
		return drawableState;
	}
}

You can use this class and simply set the android:src attribute to the animated state-list drawable.

Also don’t forget to set the android:background of the button to use the theme value ?android:selectableItemBackgroundBorderless to get the nice ripple effect on touch.

To conclude, you can still use the keyframe approach when doing animated state-list transition if you actually want to invest the time crafting each intermediary state maybe because it does a complex movement that can’t be naively interpolated.

But if you don’t need to, we now have an easy, fast and cumberless way to create beautiful state transitions using vector drawables.

Animating to Infinity

In my previous post, I covered the basics of a new type of Android drawable introduced in Lollipop for scalable graphics.

In and by itself this is already a very powerful new addition. But to see what else we can do with this, let’s reuse the vector drawable example from the previous post:

1
2
3
4
5
6
7
8
9
10
11
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="96dp"
	android:width="96dp"
	android:viewportHeight="48"
	android:viewportWidth="48" >
	<group>
		<path
			android:fillColor="@color/black_primary"
			android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
	</group>
</vector>

I didn’t touch on it at the time but you may have noticed that our path element is actually contained inside another element called a group.

For the sake of displaying vector drawable, this is not a very interesting element. Now if I paste below the list of other attributes you can set on <group/>, you should see something emerging:

Extra group attributes
rotation, scaleX, scaleY, translateX, translateY.

You will have surely recognized those are the same attributes we use to manipulate our View instances when animating them.

Enter, Animated Vector Drawable.

Animated Vector Drawables are actually more of a meta-type bridging several other pieces together much like a State List Drawable (and like the later, they also are drawables themselves).

Here is an example of an animated vector drawable that we will use later on (also stored in your drawable resource folder):

1
2
3
4
5
6
7
8
9
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:drawable="@drawable/bluetooth_loading_vector" >
	<target
		android:name="circleGroup"
		android:animation="@anim/circle_expand" />
	<target
		android:name="circlePath"
		android:animation="@anim/circle_path" />
</animated-vector>

Animated Vector Drawables are declared using the <animated-vector/> element. The first thing you need to tie in is which vector drawable the animated version is going to use as a base, this is set in the android:drawable attribute at the top level.

The rest of the file contains a bunch of different <target/> elements, those are where we are going to set up which animations are ran and on what part of the vector drawable they will be ran.

But first, here is the definition of the bluetooth_loading_vector referenced in the animated vector:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?xml version="1.0" encoding="utf-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="64dp"
	android:width="64dp"
	android:viewportHeight="192"
	android:viewportWidth="192" >
	<group>
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M87.3996,113.161L85.3665,112.191L85.3665,23.517L87.3996,22.5479L143.293,67.8542L87.3996,113.161ZM99.9498,84.2151L120.134,67.8542L99.9498,51.4934L99.9498,84.2151Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M87.4147,169.459L85.3665,168.484L85.3665,79.6589L87.4147,78.684L143.277,124.071L87.4147,169.459ZM99.9498,140.484L120.15,124.071L99.9498,107.659L99.9498,140.484Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M43.8495,129.345L103.262,81.2217L112.186,92.2392L52.7734,140.362L43.8495,129.345Z" />
		<path
			android:fillColor="@color/vector_foreground_color"
			android:pathData="M43.8495,62.2562L103.262,110.379L112.186,99.3616L52.7734,51.2387L43.8495,62.2562Z" />
	</group>
	<group
		android:name="circleGroup"
		android:pivotX="96.0"
		android:pivotY="96.0"
		android:rotation="0">
		<path
			android:name="circlePath"
			android:strokeColor="@color/vector_foreground_color"
			android:strokeWidth="16"
			android:strokeAlpha="1"
			android:trimPathEnd="0.7"
			android:pathData="M96,5.56405C145.946,5.56405 186.436,46.0536 186.436,96C186.436,145.946 145.946,186.436 96,186.436C46.0536,186.436 5.56405,145.946 5.56405,96C5.56405,46.0536 46.0536,5.56405 96,5.56405Z" />
	</group>
</vector>

And what it looks like when rasterized:

This vector drawble is a bit longer than the previous one, but there are two main things to notice:

  • The drawable is arranged in two distinct groups
  • One group and one path have a name attached, mapping back to the animated vector’s target elements

The way we have layed-out and named our vector drawable means we can now directly reference the relevant parts we want to manipulate.

For instance in our case, we are only interested in the ring around the Bluetooth logo which is why only this piece is actually named in our vector drawable.

To close the loop (so to speak), here are the two animation definition files referenced by the animated vector drawable (placed each in your anim resource folder):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!-- Resources\anim\circle_expand.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="scaleX"
		android:valueFrom="1.0"
		android:valueTo="2.5"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="scaleY"
		android:valueFrom="1.0"
		android:valueTo="2.5"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="rotation"
		android:valueFrom="0"
		android:valueTo="360"
		android:valueType="floatType"
		android:repeatCount="-1" />
</set>

<!-- Resources\anim\circle_path.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="trimPathEnd"
		android:valueFrom="0"
		android:valueTo="1"
		android:valueType="floatType"
		android:repeatCount="-1" />
	<objectAnimator
		android:duration="@integer/loading_anim_time"
		android:propertyName="strokeAlpha"
		android:valueFrom="1"
		android:valueTo="0"
		android:valueType="floatType"
		android:repeatCount="-1" />
</set>

Since this is meant as a continous animation, we must ensure that the repeat count of each animator is -1 (a synonym for ‘infinite’) so that the process runs forever.

In the remaning bits, we are simply animating a bunch of property on both the group (for transformations) and on the path (for the stroke style) like we would on a normal view.

The last thing to do is load the animated vector drawable into a continuously animating View, typically a ProgressBar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/colorPrimary"
    android:padding="48dp"
    android:clipChildren="false"
    android:clipToPadding="false">
    <ProgressBar
        android:indeterminate="true"
        android:indeterminateDrawable="@drawable/animated_bluetooth_loading"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
</FrameLayout>

And we arrive at this result:

Next up, we will see how we can apply some of this to make terrific transitions.

Scaling to Infinity

When I make an app, I spend my time between two worlds.

In the first, I produce sequences of meaningul characters for a virtual machine in your favorite IDE, Xamarin Studio. In the second, I use my best friend Inkscape (and lately also Affinity) to help me think, iterate and produce the front face of my app.

The main challenge of this form of development is to find the best way to ease the content transition between both worlds.

In Android, what’s essentially the apocalypse of screen form-factor makes it particularily painful to work with that visual assets part of your app.

Where the world was happily spread between ldpi, mdpi and hdpi a few years back, today we just don’t know how many ‘x’ we are going to be able to fit in front of ‘hdpi’ (at the time of this writing we reached xxxhdpi).

I have actually already talked about this subject and, at the time, released my own library to try to tackle this issue.

But today we can finally rejoice as Google gave developers a blessed way to create unlimited scalable graphics with the recent release of Lollipop (or Android 5.0).

This new types of drawable is unoriginaly called Vector Drawables and, as with any other drawable, they are natively understood by the Android toolchain which make their use as seamless as feasible (i.e. you can reference them in layouts, styles, acquire them through Resources.GetDrawable, …).

This is an example of a vector drawable (store them in your Resources/drawable folder):

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
	android:height="96dp"
	android:width="96dp"
	android:viewportHeight="48"
	android:viewportWidth="48" >
	<group>
		<path
			android:fillColor="@color/black_primary"
			android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
	</group>
</vector>

Which draws this well known media player action icon:

The beef of a vector drawable is in its various <path/> elements and more precisely its pathData attribute. As arcane as it may look, it actually defines the full blown shape of the icon in the very concise SVG Path data format.

Don’t worry too much though, you won’t have to write that down by yourself (you may need to tweak it though for more funky things but more on that in a later post).

Since it’s based on the same specification than used in SVG itself, as long as you can grab a SVG version of your graphic you will directly be able to copy and paste it into a vector drawable XML.

As a matter of fact, the icon example that I pasted above came from Google’s official material design icon pack which also contains SVG for every icon:

1
2
3
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48">
    <path d="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z"/>
</svg>

Notice also how the viewportHeight and viewportWidth values in the vector drawable reflect what’s in the width/height/viewBox parameter of the SVG. As long as those values are correctly defined, you can play with the android:width and android:height part of your drawable the way you want.

Together with the new general tinting facility in Lollipop, you can now iterate over your assets even faster without leaving your IDE and without having to re-generate 6 variants of your graphics.

Ada Lovelace Day 2014

Today is Ada Lovelace day, a special occasion to recognize women in our community. I want to take this opportunity to also highlight someone exceptional I have known for a while.

Her name is Andrea Magnorsky.

Andrea Magnorsky

Andrea is an Irish .NET programmer. She’s currently following one of her passion – game development – and is closing production with her studio Digital Furnace (ex-BatCat) on their next title Onikira.

To say that Andrea is a community person is an understatement. In the span of a few years she has not only created popular event/group like Global Gamecraft (game jam contest) or Dublin ALT.NET but she is also continously looking for new ways to teach people through numerous talks and meetups like Functional Kats.

She is curious of everything and always avid to learn. These days you can see her sailing along with F# and functional programming for instance.

It’s no surprise that she was recognized several times in Ireland as a top person in her field. Microsoft also recently awarded her an MVP award.

It’s thanks to people like Andrea that I’m happy to be part of the community that is software development.

Muncho

Muncho would have loved to conclude this but he had another emergency

A Tale of Connected Dots

This past week, we organized Evolve 2014 in Atlanta to talk about all things mobile with Xamarin.

Being a mobile conference, we of course made our own app so that attendees could track out the sessions and the geography of the event.

We also launched something a bit different in the form of a treasure hunt app. Thanks to Estimote, we used a combination of iBeacon and QR codes spread throughout the conference intertwined with challenges along the way until you reach the end of the quest and a special prize.

The main interface of that quest app is a serie of connected dots:

Evolve Quest screenshot

As you progress through the game, the content scrolls naturally to reveal more odd-shaped arrangement of those dots.

Originally, this screen was just a big tall image containing everything. This is suboptimal on Android for a couple of reason like some graphic chip not supporting big “texture” or simply the need to rescale at runtime on most screen.

Ultimately it’s also not fun because there is really nothing you can do with a big image.

Hint: this is NOT what we shipped (on Android at least).

Rather, what attendees could see on their Android phone at the conference was this:

A much more satisfying, softly animated, connected system of dots. The current game position is also highlighted with a discrete pulse.

We will open-source the entire Quest application at some point but I wanted to share how we did that specific bit that is entirely exclusive to Android.

View Source Code

Since this is a really custom animation, it’s a bit different than the other type of animation samples I have already shown.

For this type of thing, I usually rely on a very simple animator that gives me a floating value between 0 and 1 so that I can do my own tweening in code. I also manage to pass this value by hijacking one of the standard view animatable property so that I don’t need to expose more information to the Java bridge:

1
2
3
4
5
6
7
8
9
10
11
12
13
// Interpolator instantiation
shiverAnimator = ObjectAnimator.OfFloat (this, "scaleX", 0, 1);
shiverAnimator.RepeatCount = ValueAnimator.Infinite;
shiverAnimator.AnimationRepeat += (sender, e) => shiverIndex++;

// Property hijack
public override float ScaleX {
	get { return currentShiverInterpolation; }
	set {
		currentShiverInterpolation = value;
		Invalidate ();
	}
}

In our case, I then use this value to compute for each drawing pass an offset that is added to the base position of every dots:

1
2
3
4
5
6
7
8
9
10
11
// Expressed in dp units
int[] shiverOffsets = new[] { 9, 3, -8, 4, -7, 6 };

// Extra applied offset code
int ApplyShivering (int dotIndex, int value)
{
	var off = shiverOffsets [(dotIndex + shiverIndex) % shiverOffsets.Length]
		* currentShiverInterpolation;
	value += (int)Math.Round (off);
	return value;
}

The wave that you see on the highlighted pin comes from a GradientDrawable that I create from a shape drawable XML definition as follow:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
	android:shape="ring"
	android:useLevel="false"
	android:innerRadiusRatio="2">
	<solid android:color="#99ffffff" />
</shape>

The reason for using a drawable like this is to avoid redoing common drawing code and also taking advantage of the ring shape type automatic radius scaling (notice the innerRadiusRatio attribute) to create a nice spreading effect as the ring grows larger.

The ring size and color is dynamically computed from a slightly interpolated (quadratic or accelerated) value coming from our shared shiver interpolator:

1
2
3
4
5
6
7
8
9
10
11
var dur = shiverAnimator.Duration / 4;
var fraction = (float)(shiverAnimator.CurrentPlayTime % dur) / dur;
// Cheap interpolation
fraction *= fraction;

var rippleRad = (int)(radius * (5 * fraction + 1)) - 8;
ripple.SetBounds (x - rippleRad, y - rippleRad, x + rippleRad, y + rippleRad);

var color = Color.Argb ((int)Math.Round (0x99 * (1 - fraction)),
                        0xFF, 0xFF, 0xFF);
((GradientDrawable)ripple).SetColor (color.ToArgb ());

If you have been to my Mastering Time and Space session, you’ll likely also have detected the presence of a custom interpolator in there that is set on the shared shivering animator. The code is following:

1
2
3
4
5
6
7
8
class TriangleWave : Java.Lang.Object, ITimeInterpolator
{
	public float GetInterpolation (float input)
	{
		var t = input * 2;
		return (float)(2 * Math.Abs (t - Math.Floor (t + 0.5)) * (1 - 2 * Math.Floor (t)));
	}
}

This interpolator generates a triangle pattern that is repeatable (i.e. it both starts and ends at the same position for the dot) so that the animation can run continuously:

Triangle curve