Android Animations Tutorial 7: The secret of fillBefore, fillAfter and fillEnabled

When reading though the documentation for View Animations you might stumble across the three flags fillBefore , fillAfter and fillEnabled . You can set the flags in the XML resource of an animation, something like this.

<scale android:duration="300" android:fillAfter="true" android:fillBefore="false" android:fillEnabled="true" android:fromXScale="0.0" android:fromYScale="0.0" android:toXScale="1.0" android:toYScale="1.0" />

Or you can programmatically set the flags like this

ScaleAnimation scale = new ScaleANimation(0.0f, 1.0f, 0.0f, 1.0f); scale.setFillAfter(true); scale.setFillBefore(false); scale.setFillEnabled(true);

At first, the documentation seems reasonable. fillAfter determines whether the final transformation is applied after the animation has finished and fillBefore determines whether the initial transformation is applied before the animation starts. However, the value of fillBefore only has any influence if fillEnabled is set to true . If fillEnabled is false the transformation will be applied before the start of the animation, no matter what the value of fillBefore is.

This means that, in order to prevent the system from applying the transformation before the start of the animation you have to set fillEnabled to true and fillBefore to false . In any other case the transformation will be applied.

According to the documentation, the fillEnabled flag only influences the behaviour of the fillBefore flag. You may wonder why there are two flags controlling one single behaviour. In essence one flag should be enough. In this brilliant article Chet Haase explains that this is due to the evolution of the behaviour between API levels combined with the requirement that compatibility is preserved. He also refers to an error in the documentation that was supposedly corrected in 2011.

It turns out that the documentation is still wrong! fillEnabled does, in fact, also influence the behaviour of the fillAfter flag, but in a more subtle way.

Sequences of Animations in Sets

The real behaviour comes to light when you create sequences of animations. As I showed in my last post, we can combine animations to be played on after another inside an animation set using the startOffset property. Let’s take a look at the following animation.

<set android:shareInterpolator="true" android:fillBefore="true" android:fillAfter="false"> <translate android:fillEnabled="true" android:duration="300" android:fromXDelta="-100" android:fromYDelta="0" android:toXDelta="100" android:toYDelta="0" /> <translate android:duration="300" android:startOffset="300" android:fromXDelta="0" android:fromYDelta="-100" android:toXDelta="0" android:toYDelta="100" /> </set>

The animation consists of two translate animations, the first moves the view in the x direction and the second moves the view in the y direction. The second animation in the set has an offset of 300 milliseconds, so the two animations are played in sequence. Let’s call the first translation transX and the second translation transY .

First of all, let’s look again at the behaviour of fillBefore . As stated above the only way to avoid the transformation to be applied before the animation starts is to set fillEnabled to true and fillBefore to false . The documentation states that the values of fillBefore and fillAfter are pushed down from the animation set. On the other hand, it is the value of fillEnabled of the individual transformation that has an impact on the behaviour. So, to avoid the transformation transY to be applied before the start of the animation one has to set the following flags.

set.fillBefore = false transY.fillEnabled = true

The undocumented behaviour of fillAfter

The documentation, in its current form, will make you believe that the fillAfter property of the animation set will influence whether the transformation is applied after the animation has finished. However, we have to distinguish between the end of the complete animation and the end of the individual animation. In the example above the first translation transX finishes before the end of the complete animation. If set.fillAfter is true then all the transformations will be applied after the animations defining them have finished.

If, on the other hand, set.fillAfter is false then the transformations will not be applied after the complete animation set has finished. But it is then up to transX.fillEnabled to specify if the transformation transX is applied in the time between the end of the first translate animation and the end of the complete set of animations.

This is all a bit confusing. The behaviour of fillBefore and fillAfter is not symmetric. It took me quite some time to get my head around this issue. So I have created the figure below, where I have tried to illustrate how the flags influence the application of the transformations.

The animation set, set , contains two animations, anim1 and anim2 . anim1 plays first and then anim2 . The end of anim2 determines the end of the complete animation. The figure represents the timeline. The yellow boxes indicate the times when the individual animations are playing. Outside of the yellow areas I have specified under which conditions the transformations of anim1 and anim2 are applied.

In the time before the start of the animation, the initial transformation of anim1 will be applied if (and only if)

(set.fillBefore || !anim1.fillEnabled) == true

Similarly, the initial transformation of anim2 will be applied if

(set.fillBefore || !anim2.fillEnabled) == true

After both animations have finished the final transformations of both anim1 and anim2 will be applied if

set.fillAfter == true

However, in the time after anim1 has finished but anim2 is still running, the final transformation of anim1 will be applied if

(set.fillAfter || !anim1.fillEnabled) == true

This means that, under default conditions, the transformation of anim1 is still applied during this time even if set.fillAfter==false . But setting anim1.setFillEnabled to true will prevent the transformation from being applied right after anim1 has finished if set.fillAfter==false .

All of this means that you have slightly more fine grained control over the behaviour after the end of the animations than you have before the start of the animations. Why this asymmetry has been introduced will probably remain a mystery.

Follow the author

