20

Could anyone tell me how they have got RotateDrawable to work whether it be from code or XML or both? The documentation on animating Drawables is pretty poor and animation only seems to work for images. I want to be able to animate all drawables. When i tried to get a RotateDrawble from XML is just causes an exception. What is the correct function to find a RotateDrawable from XML?

Machavity
  • 30,841
  • 27
  • 92
  • 100
D-Dᴙum
  • 7,689
  • 8
  • 58
  • 97
  • http://stackoverflow.com/questions/4763103/rotating-a-drawable-in-android – baboo Feb 26 '13 at 08:08
  • i don't understand the question. do you want to have an animation, or just rotate the image within? for rotation, check out this post: http://stackoverflow.com/a/21376008/878126 – android developer Jan 27 '14 at 09:26

10 Answers10

19

here's a nice solution for putting a rotated drawable for an imageView:

Drawable getRotateDrawable(final Bitmap b, final float angle) {
    final BitmapDrawable drawable = new BitmapDrawable(getResources(), b) {
        @Override
        public void draw(final Canvas canvas) {
            canvas.save();
            canvas.rotate(angle, b.getWidth() / 2, b.getHeight() / 2);
            super.draw(canvas);
            canvas.restore();
        }
    };
    return drawable;
}

usage:

Bitmap b=...
float angle=...
final Drawable rotatedDrawable = getRotateDrawable(b,angle);
root.setImageDrawable(rotatedDrawable);

another alternative:

private Drawable getRotateDrawable(final Drawable d, final float angle) {
    final Drawable[] arD = { d };
    return new LayerDrawable(arD) {
        @Override
        public void draw(final Canvas canvas) {
            canvas.save();
            canvas.rotate(angle, d.getBounds().width() / 2, d.getBounds().height() / 2);
            super.draw(canvas);
            canvas.restore();
        }
    };
}

also, if you wish to rotate the bitmap, but afraid of OOM, you can use an NDK solution i've made here

Community
  • 1
  • 1
android developer
  • 114,585
  • 152
  • 739
  • 1,270
12

You have to animate the "level" property, where 0 is the start value and 10000 is the end value.

The below example animates from start to finish, you can reverse the animation easily with this method.

final RotateDrawable rotateDrawable = ...
ObjectAnimator.ofInt(rotateDrawable, "level", 0, 10000).start();
Mark Hetherington
  • 1,612
  • 14
  • 24
  • 1
    A good year later this information is available here: https://developer.android.com/reference/android/graphics/drawable/Drawable.html#setLevel(int) – bogl Sep 26 '19 at 20:38
7

I would like to add a full example of animating a progress icon on ImageView, it is based on Mark Hetherington answer.

So my animation looks as follows:

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
                 android:pivotX="50%"
                 android:pivotY="50%"
                 android:fromDegrees="0"
                 android:toDegrees="-360"
                 android:duration="100"
                 android:drawable="@drawable/ic_loop_black_24dp"
    />

icon comes from https://material.io/icons/

then my layout contains an ImageView as follows:

        <ImageView
            android:id="@+id/progress"
            android:layout_marginTop="0dp"
            android:layout_marginLeft="-3dp"
            android:layout_width="30dp"
            android:layout_height="30dp"

            android:visibility="gone"
            android:scaleType="fitCenter"
            android:background="@drawable/progress_anim"
            android:layout_gravity="center_horizontal|center_vertical"
            />

and finally in code when I need to show animation I do:

    RotateDrawable rotateDrawable = ((RotateDrawable)progressImage.getBackground());
    ObjectAnimator anim = ObjectAnimator.ofInt(rotateDrawable, "level", 0, 10000);
    anim.setDuration(1000);
    anim.setRepeatCount(ValueAnimator.INFINITE);
    anim.start();
marcinj
  • 48,511
  • 9
  • 79
  • 100
4

RotateDrawable does not seem to be animated. Instead, you have to use setLevel to change the rotation of the drawable.

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/your_drawable"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="360" />

And set the level will rotate the drawable:

final ImageView image = (ImageView)findViewById(R.id.imageView1);
final RotateDrawable drawable = (RotateDrawable)image.getDrawable();
drawable.setLevel(500);
Carson Reinke
  • 713
  • 5
  • 16
  • I must still be missing something here. First what are you doing with the tag. Your not inflating it anywhere. And second I try to cast a drawable to a RotateDrawable and get the following cast exception java.lang.ClassCastException: android.graphics.drawable.BitmapDrawable – Frank Sposaro Dec 28 '11 at 00:58
  • I guess my example assumes that there is a layout and that layout is using an ImageView with the RotateDrawable. If you have a bitmap, the RotateDrawable references it, not the layout. – Carson Reinke Jan 01 '12 at 01:14
  • 2
    How do you set how fast the animation is? I've tried various values for "duration", but it doesn't seem to do anything... – android developer May 10 '16 at 07:43
3

The following code returns a Drawable wrapper that rotates another Drawable programmatically:

Drawable rotateDrawable(Drawable d, final float angle) {
    // Use LayerDrawable, because it's simpler than RotateDrawable.
    Drawable[] arD = {
        d
    };
    return new LayerDrawable(arD) {
        @Override
        public void draw(Canvas canvas) {
            canvas.save();
            canvas.rotate(angle);
            super.draw(canvas);
            canvas.restore();
        }
    };
}
Adam Gawne-Cain
  • 1,347
  • 14
  • 14
2

Since you're trying to use Almero's Android Gesture Detectors, I decided to do the same in order to find an appropriate solution:

public class MainActivity extends Activity {

    private RotateGestureDetector mRotateDetector;
    private float mRotationDegrees = 0.f;
    private static final float ROTATION_RATIO = 2;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mRotateDetector = new RotateGestureDetector(getApplicationContext(), new RotateListener());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mRotateDetector.onTouchEvent(event);
        return super.onTouchEvent(event);
    }

    private class RotateListener extends RotateGestureDetector.SimpleOnRotateGestureListener {
        @Override
        public boolean onRotate(RotateGestureDetector detector) {
            mRotationDegrees -= detector.getRotationDegreesDelta();
            ImageView v = (ImageView) findViewById(R.id.imageView);
            // For NineOldAndroids library only!
            ViewHelper.setRotation(v, mRotationDegrees * ROTATION_RATIO);
            // For HONEYCOMB and later only!
            v.setRotation(mRotationDegrees * ROTATION_RATIO);
            return true;
        }
    }
}

It works fine for me (I'm able to rotate the ImageView with two-finger rotation gesture. NOTE: Don't forget to chose appropriate rotation method call. I commented both of them to draw your attention.

ROTATION_RATIO is just a multiplier to speed-up a rotation response on my fingers movement. You can use any rotation axis (setRotation(), setRotationX() and setRotationY()) methods for a View.

To enable support of this code on Android devices with API Level lower than 11 (pre-Honeycomb devices) you may want to engage NineOldAndroid library.

QArea
  • 4,955
  • 1
  • 12
  • 22
2

You could manually call RotatedDrawable.setLevel() to rotate the drawable, or you could read the code of ProgressBar, the indeterminate drawable is a LayerDrawable whose children were RotatedDrawable, like this one:

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

The rotate animation was driven by ProgressBar's onDraw method.

liuyong
  • 987
  • 10
  • 19
2

This is a good working example. Param duration is used to animate it.

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="4000"
    android:fromDegrees="0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toDegrees="720" >

    <shape
        android:innerRadius="20dp"
        android:shape="ring"
        android:thickness="4dp"
        android:useLevel="false" >
        <size
            android:height="48dp"
            android:width="48dp" />

        <gradient
            android:centerY="0.5"
            android:endColor="@android:color/white"
            android:startColor="#00ffffff"
            android:type="sweep"
            android:useLevel="false" />
    </shape>

</rotate>
RexSplode
  • 1,475
  • 1
  • 16
  • 24
2

I haven't worked with a RotateDrawable, but if you're simply trying to animate rotation on a graphic, you don't need it. Drawables with a 'level' like RotateDrawable are meant to convey information rather than animate views.

The following code rotates an ImageView around its center:

ImageView myImageView = (ImageView)findViewById(R.id.my_imageview);

AnimationSet animSet = new AnimationSet(true);
animSet.setInterpolator(new DecelerateInterpolator());
animSet.setFillAfter(true);
animSet.setFillEnabled(true);

final RotateAnimation animRotate = new RotateAnimation(0.0f, -90.0f,
    RotateAnimation.RELATIVE_TO_SELF, 0.5f, 
    RotateAnimation.RELATIVE_TO_SELF, 0.5f);

animRotate.setDuration(1500);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);

myImageView.startAnimation(animSet);
David Snabel-Caunt
  • 57,804
  • 13
  • 114
  • 132
  • 3
    Thanks for your reply David. If I understand your method correctly, this rotates the WHOLE view whereas I'd ultimately like to be able to rotate/animate a single Drawable in a view for example a pointer rotates over a background which is fixed as it is the scale for a dial. This is why I'm trying to use the RotateDrawable but unfortunately can't understand the docs! – D-Dᴙum May 03 '11 at 17:54
0

If you want to rotate drawable forever you can use animated-rotate tag in drawable xml like this.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <animated-rotate android:drawable="@drawable/ic_refresh" android:pivotX="50%" android:pivotY="50%" />
    </item>
</selector>
RHS.Dev
  • 412
  • 6
  • 18