Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Animate Two Layer Drawable Items Pre-Honeycomb

I am trying to animate two layers of a drawable to achieve the effect of the post-Honeycomb indeterminate progress indicator. The XML is very straightforward but it would seem that only one layer will animate when run on platforms prior to Honeycomb.

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
             android:drawable="@drawable/abs__spinner_48_outer_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="0"
             android:toDegrees="1080" />
    </item>
    <item>
        <rotate
             android:drawable="@drawable/abs__spinner_48_inner_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="720"
             android:toDegrees="0" />
    </item>
</layer-list>

Is this simply a limitation of these platforms or is there an alternate syntax that I can use (either in general or specifically targetted at pre-API11) to achieve the desired functionality?

like image 728
Jake Wharton Avatar asked Dec 31 '11 07:12

Jake Wharton


2 Answers

There is indeed a platform limitation, although it's not what you might think. The issue is that pre-API11, RotateDrawable had some crude code in it to require that the animation rotate clockwise by checking if toDegrees was greater than fromDegrees; if not, the two were forced equal to each other. If you modified your example to have the second item move in a forward direction (from 0 to 720, or even -720 to 0), both images would animate fine on all platforms; though I realize that defeats the purpose of what you're aiming for.

Take a look at the cached version Google Codesearch has of RotateDrawable.inflate(), which is the 2.3 version of the method used to turn the XML into the object, and you'll see what I mean.

RotateDrawable.java ...the offending code is around line 235...

    float fromDegrees = a.getFloat(
            com.android.internal.R.styleable.RotateDrawable_fromDegrees, 0.0f);
    float toDegrees = a.getFloat(
            com.android.internal.R.styleable.RotateDrawable_toDegrees, 360.0f);

    toDegrees = Math.max(fromDegrees, toDegrees); //<--There's the culprit

This takes an XML block like the second item that you have there, and turns it into a RotateDrawable that ends up with the same value for fromDegrees and toDegrees (in your case, 720), causing the image to simply stand still. You can visible test this by setting the start value to some value not a multiple of 360 (like 765). You'll see that the image still does not animate, but is rotated to the initial coordinate.

This awkward check was removed in the Honeycomb/ICS sources, which is why you can do backwards rotation on those platforms. Also, it doesn't look like there is a way to set these values from Java code, so a custom RotateDrawableCompat may be in your future :)

HTH

like image 106
devunwired Avatar answered Sep 19 '22 21:09

devunwired


Seems like the quick and dirty solution to get this working in pre honeycomb is to just flip the from and to in the second rotate. This is not ideal but at least the thing spins around (even if it's a bit more "boring"). This is how ABS seems to have solved it.

  <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
             android:drawable="@drawable/abs__spinner_48_outer_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="0"
             android:toDegrees="1080" />
    </item>
    <item>
        <rotate
             android:drawable="@drawable/abs__spinner_48_inner_holo"
             android:pivotX="50%"
             android:pivotY="50%"
             android:fromDegrees="0"
             android:toDegrees="720" /> <!-- Like this -->
    </item>
  </layer-list>
like image 24
dmon Avatar answered Sep 20 '22 21:09

dmon