Smoothly animating TextView's text color

Starting Android 3.0 (Honeycomb), the framework provides developers with a bunch of utilities to animate View properties such as their x/y position, background color, alpha, etc. Animating objects is a vast and subtle topic in mobile development. Animations are an excellent way to indicate users how a UI "transformed" itself from one state to the other but performing to many animations at the same time may also completely waste the user experience.

In this article, I want to show you how to animate the text color of a TextView. Although this may seem easy at first sight, it requires some little workarounds if you want the animation to perform correctly. In order to demonstrate the code, we will use a simple example that perform a smooth text color transition from black to red.

Attempt #1

Let's begin with the simpliest code ever that you can use to animate a value. It consists on getting a reference on the TextView, creating an ObjectAnimator on the "textColor" property and starting it:

final TextView textView = (TextView) findViewById(R.id.text);
textView.setTextColor(Color.BLACK);

final ObjectAnimator animator = ObjectAnimator.ofInt(textView, "textColor", Color.RED);
animator.setDuration(333L);
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new DecelerateInterpolator(2));
animator.start();

This code runs perfectly and, as, expected, it animates the text color. But it animates the text color from transparent to red although I've explicitly set the text color to black prior starting the ObjectAnimator. Because we don't want to deep dive into the code (at least for now), let's workaround this issue by using a different implementation.

Attempt #2

Android introduced a new way to encapsulate a property in API 14: Property[0]. This class is extremely handy and lets you create a new instance just by calling the "of(Class<T>, Class<V>, String)" static factory method:

final TextView textView = (TextView) findViewById(R.id.text);
textView.setTextColor(Color.BLACK);

final Property<TextView, Integer> property = Property.of(TextView.class, int.class, "textColor");
final ObjectAnimator animator = ObjectAnimator.ofInt(textView, property, Color.RED);
animator.setDuration(333L);
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new DecelerateInterpolator(2));
animator.start();

If you run this code, you will end up with an annoying but understandable Exception:

android.util.NoSuchPropertyException: No accessor method or field found for property with name textColor

To sum up, this Exception informs us TextView has no "int getTextColor()". Indeed, looking at the TextView's API, we can notice it has a "ColorStateList getTextColors()" method but no "int getTextColor()". Fortunately, that doesn't mean we're stuck : "int getCurrentTextColor()" is here to the rescue.

Attempt #3 ... the finale one

Because TextView has no "int getTextColor()" method, we need to create a completely custom Property that will forward the set/get method to the appropriate TextView methods:

final TextView textView = (TextView) findViewById(R.id.text);
textView.setTextColor(Color.BLACK);

final Property<TextView, Integer> property = new Property<TextView, Integer>(int.class, "textColor") {
   @Override
    public Integer get(TextView object) {
        return object.getCurrentTextColor();
    }

   @Override
    public void set(TextView object, Integer value) {
        object.setTextColor(value);
    }
};

final ObjectAnimator animator = ObjectAnimator.ofInt(textView, property, Color.RED);
animator.setDuration(333L);
animator.setEvaluator(new ArgbEvaluator());
animator.setInterpolator(new DecelerateInterpolator(2));
animator.start();

Running the code above, we finally have a smooth animation from black to red. 

Conclusion

Although, the solution we endup is based on Property - a Android 4.0+ API - it doesn't mean you can't animate TextView's text color pre-API 14. The solution pre-API 14 is a little bit trickier though and consists on manually listening to "animation ticks" and changing the text color manually.

The Property-based solution didn't explain us why the first attempt was animating from transparent to red rather than black to red. Put simply, ObjectAnimators based on property names use a completely different algorithm to determine the getters and setters. When using property names the code accepts non-existing setters/getters[3] while Property-based ObjectAnimators are more strict and require the setter and getter to exist[4]. Because of this, we were actually animating from an unknown color of value 0x0 (i.e. Color.TRANSPARENT) to the final red color.

In a nutshell, I strongly encourage you to use Property when animating values. You can seriously consider the old property name-based APIs as deprecated. This is also a great opportunity for you to set the min SDK of your application to 14!.

[0]: http://developer.android.com/reference/android/util/Property.html
[1]: http://developer.android.com/reference/android/widget/TextView.html#getTextColors()
[3]: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/animation/PropertyValuesHolder.java#L378-L422
[4]: https://github.com/android/platform_frameworks_base/blob/master/core/java/android/util/ReflectiveProperty.java#L45-L88

#gde   #android   #blogpost
Shared publiclyView activity