Awaitable Animations on Android

A tiny extension method allows you to await Android animations

Posted by Sven-Michael Stübe on September 20, 2016

Xamarin has done a great job on bringing async/await to animations on iOS (as Kerry shows on his blog) and on Xamarin.Forms. For my Fingerprint Plugin I was playing around with some animations on Android and I expected to find a similar animation extension - but I did not (If I’ve overseen something, tweet me). But, awaiting animations is pretty easy with a small extension method.

Problem

I want to colorize a icon, animate it and remove the color after the animation. In another use case I want to animate the icon and close the dialog afterwards.

Solution

The Android way would be to implement a IAnimatorListener, set the color in its OnAnimationStart method and reset it in OnAnimationEnd. But with this approach the code isn’t readable linearly and you would have to implement a IAnimatorListener for each use case. I wanted to write my Animation like this:

_icon.SetColorFilter(NegativeColor);
var shake = ObjectAnimator.OfFloat(_icon, "translationX", -10f, 10f);
shake.SetDuration(500);
shake.SetInterpolator(new CycleInterpolator(5));
await shake.StartAsync();
_icon.ClearColorFilter();

You only need these two classes and are ready to animate Android with C# style.

Extension

The AnimatorExtensions only contains the StartAsync method that you should use to start a animation. It adds a TaskAnimationListener as listener, starts the animation and returns the a Task. The method can be used with all Animator classes (e.g. ObjectAnimator, AnimatorSet, ValueAnimator)

public static class AnimatorExtensions
{
    public static Task StartAsync(this Animator animator)
    {
        var listener = new TaskAnimationListener();
        animator.AddListener(listener);
        animator.Start();
        return listener.Task;
    }
}

TaskAnimationListener

The TaskAnimationListener implements IAnimatorListener and uses a TaskCompletionSource to convert the events OnAnimationCancel and OnAnimationEnd to the Canceled or Completed states of a Task.

public class TaskAnimationListener : Java.Lang.Object, Animator.IAnimatorListener
{
    private readonly TaskCompletionSource<int> _tcs;
    public Task Task => _tcs.Task;

    public TaskAnimationListener()
    {
        _tcs = new TaskCompletionSource<int>();
    }

    public void OnAnimationCancel(Animator animation)
    {
        _tcs.TrySetCanceled();
    }

    public void OnAnimationEnd(Animator animation)
    {
        _tcs.TrySetResult(0);
    }

    public void OnAnimationRepeat(Animator animation)
    {
    }

    public void OnAnimationStart(Animator animation)
    {
    }
}

Result

Found a typo? Send me a pull request!