Android Property Animations: Evaluators

Until now we have been animating float or int properties only. The Property Animation System is, however, not limited to only simple numeric types. Out of the box, you can also animate ARGB colours and Rect objects by using the ArgbEvaluator and the RectEvaluator classes. In fact, you can animate any kind of object if you write your own evaluator class. In this post I will be showing a few examples and explain how to use evaluators to animate other object types. To provide examples, I will create two evaluators. The first will animate between two Matrix transformations. The second will animate between two colours using the HSV colour model.

Using Evaluators

In the previous examples we created object animators by calling the static methods ObjectAnimator.ofFloat and ObjectAnimator.ofInt. These two methods return object animators that are set up to animate float and int properties of an object. Implicitly the animators will use the FloatEvaluator and the IntEvaluator classes. These classes will convert a floating point number, usually in the range between 0.0 and 1.0 to a float or int that lies between the start point and end point of the animation. In other words, the evaluator takes an input value from the output of the interpolator and converts it into the type that the animated object can understand.

Android has two other evaluators built in. The RGBEvaluator can be used to set colour properties of objects. The RectEvaluator, which is available since API version 18 is used to animate Rect properties. To create an object animator that animates a Rect or colour property we can use the ObjectAnimator.ofObject method. This method is very similar to the  ObjectAnimator.ofFloat and ObjectAnimator.ofInt methods. The only difference is that we have to pass the evaluator explicitly.

Let’s create a colour animation by animating the background colour of a View from red to blue.

View someView = findViewById(R.id.some_view);

ObjectAnimator anim = ObjectAnimator.ofObject(someView, 
                                              "backgroundColor", 
                                              new ArgbEvaluator(), 
                                              Color.RED, Color.BLUE);

anim.setDuration(2000);
anim.start();

 

Animation using the RGBEvaluator

Animation using the RGBEvaluator

The only line that is new compared to previous tutorials is the second line. ObjectAnimator.ofObject takes the evaluator as third argument. The fourth and the following arguments specify the values that the animator should take as start value, end value and the values of any way points between the start and end. You can specify an arbitrary number of values to animate through, as described in the tutorial on programming animations. The RGBEvaluator will separate the colours into their red, green and blue components. These will be animated separately. The result can be seen on the right.

As an example of using the RectEvaluator, we will animate the clipBounds property of an ImageView. Note that the RectEvaluator is only available from API 18 onwards so you should set your minSdkVersion in the manifest accordingly.

View someImage = findViewById(R.id.some_image);
Rect local = new Rect();
someImage.getLocalVisibleRect(local);
Rect from = new Rect(local);
Rect to = new Rect(local);

from.right = from.left + local.width()/4;
from.bottom = from.top + local.height()/2;

to.left = to.right - local.width()/2;
to.top = to.bottom - local.height()/4;

To create an animation using rectangle values we need to define some rectangles for the start and end values of the simulation We first obtain the Rect that describes the visible part of the ImageView. This is done by creating a Rect called local and then passing it to getLocalVisibleRect. The two rectangles from and to are created as copies of the local rectangle. The bounds of from and to are changed so that from sits in the top left hand corner of the view and to sits in the bottom right hand corner.

ObjectAnimator anim = ObjectAnimator.ofObject(someImage, 
                                              "clipBounds", 
                                              new RectEvaluator(), 
                                              from, to);

anim.setDuration(2000);
anim.start();
Animation using RectEvaluator

Animation using RectEvaluator

Creating the ObjectAnimator is again just a call to ObjectAnimator.ofObject. This time we pass a new RectEvaluator as third argument. The two Rect objects from and to are passed as fourth and fifth argument.

When the animation starts, the image will be clipped to a rectangular region. This rectangular region changes over time as the animation proceeds. The result can be seen on the right.

Creating Evaluators

Sometimes the evaluators supplied by the standard Android API are not enough. In this case you can simply define your own. All you need to do is implement the TypeEvaluator<T> interface. This is a generic interface. The type parameter T should be the type of the property that you want to animate. The TypeEvaluator<T> interface consists of a single method that needs to be implemented.

public T evaluate(float fraction, T startValue, T endValue);

The idea is that the method is given a start value and an end value and should return an interpolated value. The float fraction determines the position at which to interpolate. When fraction is 0.0 then evaluate should return the start value. When fraction is 1.0 then it should return the end value. For any number between 0.0 and 1.0 is should return an object that lies somewhere between the start value and the end value.

A MatrixEvaluator

Let’s look at an example. ImageView has a method called setImageMatrix. This method allows you to specify a transformation matrix that will be applied to the image before it is drawn. Transformation matrices can be used to specify rotations and scaling of the image but you can also specify a skew. The rotation and scale can also be set using setRotation or setScale. The skew, on the other hand, can’t be directly applied in any way  other than using matrix transformations. This means that, in order to animate the skew of an image we need to create an evaluator that operates on Matrix objects.

public class MatrixEvaluator implements TypeEvaluator<Matrix> {
  public Matrix evaluate(float fraction, 
                         Matrix startValue, 
                         Matrix endValue) {
    float[] startEntries = new float[9];
    float[] endEntries = new float[9];
    float[] currentEntries = new float[9];

    startValue.getValues(startEntries);
    endValue.getValues(endEntries);

    for (int i=0; i<9; i++)
      currentEntries[i] = (1-fraction)*startEntries[i] 
                          + fraction*endEntries[i];

    Matrix matrix = new Matrix();
    matrix.setValues(currentEntries);
    return matrix;
  }
}

The MatrixEvaluator class implements the TypeEvaluator with the type parameter set to Matrix. This means that we have to implement the evaluate method with T replaced by Matrix. In the first three lines we create float arrays that will hold the matrix elements for the start value, the end value and the interpolated value. Next we fill the arrays startEntries and endEntries from their corresponding matrix by calling getValues.

The loop iterates over all 9 matrix elements and simply interpolates between the start and end values using fraction. Finally we create a new Matrix object called matrix which we fill with the values and return it.

Using the new evaluator is relatively straightforward. Most of the work will in be creating the matrices needed to set the start and end values for the animation.

ImageView image = (ImageView)findViewById(R.id.some_image);
image.setScaleType(ScaleType.MATRIX);

float scale = 
  (float)image.getHeight()
    /(float)image.getDrawable().getIntrinsicHeight();
Matrix from = new Matrix();
from.setScale(scale, scale);
from.postSkew(-0.5f, 0.0f);
Matrix to = new Matrix(image.getMatrix());
to.setScale(scale, scale);
to.postSkew(0.5f, 0.0f);

We first get the ImageView from the layout. ImageView has a number of different scaling types. We need to set the scaling type to ScaleType.MATRIX so that the image will be scaled using a transformation matrix. We could have also done this in the XML layout file using the android:scaleType attribute. A scale is calculated so that the image will fit inside the view using the height of the View and the intrinsic height of the Drawable. Now we have everything to create two matrices, set their scale values and skew values.

Creating the animation object is, again, just a single line.

ObjectAnimator anim = ObjectAnimator.ofObject(image, 
                                              "imageMatrix", 
                                              new MatrixEvaluator(), 
                                              from, to);

The imageMatrix property corresponds to the setImageMatrix method. We pass a new MatrixEvaluator as third argument and the two matrices from and to. Now all we have to do is start the animation.

anim.setDuration(500);
anim.setRepeatCount(5);
anim.setRepeatMode(ObjectAnimator.REVERSE);
anim.start();
An animation using the MatrixEvaluator

An animation using the MatrixEvaluator

For a little extra effect, we repeat the animation 5 times and set the repeat mode to ObjectAnimator.REVERSE. This means that the animation will be played forward and backward 5 times. The resulting animation can be seen on the right.

A Colour Evaluator Using HSV Colour Model

We have used the RGBEvaluator in an example above. This evaluator is part of the Android SDK. Sometimes the resulting animation is not quite satisfactory. Maybe you would like to cycle through the colours using the HSV colour model. This means that an animation from red to blue will animate through the rainbow colours and include yellow and green colours on the way. Here we will create such an HsvEvaluator.

public class HsvEvaluator implements TypeEvaluator<Integer> {
  public Integer evaluate(float fraction, 
                          Integer startValue, 
                          Integer endValue) {
    float[] startHsv = new float[3];
    float[] endHsv = new float[3];
    float[] currentHsv = new float[3];

    Color.colorToHSV(startValue, startHsv);
    Color.colorToHSV(endValue, endHsv);

    for (int i=0; i<3; i++)
      currentHsv[i] = (1-fraction)*startHsv[i] + fraction*endHsv[i];

    while (currentHsv[0]>=360.0f) currentHsv[0] -= 360.0f;
    while (currentHsv[0]<0.0f) currentHsv[0] += 360.0f;

    return Color.HSVToColor(currentHsv);
  }
}

The code follows pretty much the same pattern as the MatrixEvaluator. Colours in Android are represented by 32 bit integers. This means that the HsvEvaluator should implement TypeEvaluator<Integer>. The corresponding evaluate method first converts the colours to the HSV colour model. The HSV values are stores in float arrays of length 3 corresponding to hue, saturation and value. The static method Color.colorToHsv fills the arrays for the start and end colours. The loop then interpolates the colours between the start and end values.

There is one pitfall however. The reverse transformation, from HSV back to int will clip the values if they are out of bounds. This might seem reasonable but the hue value is cyclic. This means that values above 360.0 should be mapped back into the interval from 0.0 to 360.0. In the same way a value below 0.0 should also be mapped into the interval. We can simply do this by adding or subtracting 360.0 from the number until it lies inside the interval.

You might ask how one could ever obtain values that lie outside the interval, when the start and end values will definitely be inside the interval. After all, we are interpolating between the two limiting values. Well, not quite. Remember our discussion of interpolators? The AnticipateInterpolator will produce output values below 0.0 and the OvershootInterpolator will produce values above 1.0. This means that we have to account for the case that fraction lies outside the interval from 0.0 to 1.0.

After this discussion, we can finally start our colour animation.

View square = findViewById(R.id.some_view);

ObjectAnimator anim = ObjectAnimator.ofObject(square, 
                                              "backgroundColor", 
                                              new HsvEvaluator(), 
                                              Color.RED, Color.BLUE);
anim.setDuration(2000);
anim.start();
An animation using the HsvEvaluator

An animation using the HsvEvaluator

The result of this animation can be seen on the right. Note the difference between this colour animation and the previous one which was using the RGBEvaluator.

I hope you enjoyed this weeks tutorial. As always, the code can be found on Github.

Follow the author

9 Comments

  1. Anonymous

    Very interesting and helpful article. Thanks!

    Reply
  2. vikas kumar jha

    it is very nice tutorial,
    i had one problem
    how to reset the position and size of view after animation.
    ie i want implementation like fillAfter() of animation class with object animator.
    Please help thanks in advance.

    Reply
    1. Mikail (Post author)

      If you want to influence the appearance of the view after the animation has finished you can implement an AnimatorListener and set the view’s properties in the onAnimationEnd method.

      Reply
      1. vikas kumar jha

        Thank you so much mikail. this helps me. solved my problem

        Reply
  3. vikas kumar jha

    I have one custom RelativeLayout,where i used canvas.clipRect(rect) method. and then in some other part of my program i want to animate clipbouds property using objectAnimator and RectEvaluator. But it is not working.
    just for testing purpose,if i comment my canvas.clipRect(rect),then clipbound animation work fine, please give me the solution. below is some my code.

    canvas.clipRect(new Rect(left, top, right, bottom), Region.Op.REPLACE); in custom Relativelayout.

    for animation

    Rect localRect = new Rect();
    shape.getLocalVisibleRect(localRect);
    rectFrom.right = rectFrom.left;
    rectFrom.bottom = rectFrom.height();
    Rect rectFrom = new Rect(localRect),rectTo = new Rect(localRect);
    ObjectAnimator.ofObject(shape, “clipBounds”, new RectEvaluator(), rectFrom, rectTo);
    ** Shape is my custome RelativeLayout.

    Reply
  4. fractions

    A Short History of C. elegans Research | WormClassroom

    Reply
  5. Definition

    Training Opportunities – National Children’s Advocacy …

    Reply
  6. different

    Pro Bono Patent Program – Mi Casa Resource Center

    Reply
  7. 2017

    # Garcinia Cambogia Coconut Oil – Dr Oz Pure Garcinia …

    Reply

Leave a Comment

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>