28

I am getting strange results with the following code:

iv = (ImageView) findViewById(R.id.iv);
        iv.setImageResource(R.drawable.spinner_white_76);

        Animation a = new RotateAnimation(0.0f, 360.0f,
                Animation.RELATIVE_TO_SELF, iv.getDrawable()
                        .getIntrinsicWidth() / 2, Animation.RELATIVE_TO_SELF,
                iv.getDrawable().getIntrinsicHeight() / 2);
        a.setRepeatCount(-1);
        a.setDuration(1000);

        iv.startAnimation(a);

Whats the right way to specify the axis point (center of the drawable)?

Sameer Segal
  • 21,813
  • 7
  • 42
  • 56

3 Answers3

67

Feel stupid! Got it to work after spending some time closely reading the documentation:

Animation a = new RotateAnimation(0.0f, 360.0f,
                Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        a.setRepeatCount(-1);
        a.setDuration(1000);
Sameer Segal
  • 21,813
  • 7
  • 42
  • 56
  • 17
    Adding "a.setInterpolator(new LinearInterpolator());" makes the rotation a little smoother. – Tom Quinn Jul 18 '12 at 15:00
  • 1
    I want to rotate 180 degree but no repetition. after the rotate, object will be on final state what i mean it will not come back at beginning position. How can i do that? – Omar Faroque Anik Feb 16 '15 at 21:49
  • 2
    @Md Omar Faroque Anik : add "a.setFillAfter(true);" to make the transformation persistent and "a.setRepeatCount(0);" for no repetition. – Bubu Mar 23 '15 at 16:09
6

Note that this will not work if your object has asymmetric padding (for example a left padding of 5px and a right padding of 0px), because padding is considered part of the object. Which means the computed center will be offset.

One solution to this is to use margins instead of padding if you are using padding for layout reasons. As the API says concerning margins: "This space is outside this view's bounds." (ViewGroup.MarginLayoutParams)

This means that margins will not rotate as padding does.

st4r0pr4m
  • 61
  • 1
  • 2
4

As title for this questions is Drawable Rotating around its center (and I was searching exactly for it, had not found it and had to implement it myself) would like to post my solution/answer.

I ended up writing custom drawable which can wrap any drawable and allow its rotation. Here is the code:

public class RotatableDrawable extends DrawableWrapper {

    private float rotation;
    private Rect bounds;
    private ObjectAnimator animator;
    private long defaultAnimationDuration;

    public RotatableDrawable(Resources resources, Drawable drawable) {
        super(vectorToBitmapDrawableIfNeeded(resources, drawable));
        bounds = new Rect();
        defaultAnimationDuration = resources.getInteger(android.R.integer.config_mediumAnimTime);
    }

    @Override
    public void draw(Canvas canvas) {
        copyBounds(bounds);
        canvas.save();
        canvas.rotate(rotation, bounds.centerX(), bounds.centerY());
        super.draw(canvas);
        canvas.restore();
    }

    public void rotate(float degrees) {
        rotate(degrees, defaultAnimationDuration);
    }

    public void rotate(float degrees, long millis) {
        if (null != animator && animator.isStarted()) {
            animator.end();
        } else if (null == animator) {
            animator = ObjectAnimator.ofFloat(this, "rotation", 0, 0);
            animator.setInterpolator(new AccelerateDecelerateInterpolator());
        }
        animator.setFloatValues(rotation, degrees);
        animator.setDuration(millis).start();
    }

    @AnimatorSetter
    public void setRotation(float degrees) {
        this.rotation = degrees % 360;
        invalidateSelf();
    }

    /**
     * Workaround for issues related to vector drawables rotation and scaling:
     * https://code.google.com/p/android/issues/detail?id=192413
     * https://code.google.com/p/android/issues/detail?id=208453
     */
    private static Drawable vectorToBitmapDrawableIfNeeded(Resources resources, Drawable drawable) {
        if (drawable instanceof VectorDrawable) {
            Bitmap b = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            Canvas c = new Canvas(b);
            drawable.setBounds(0, 0, c.getWidth(), c.getHeight());
            drawable.draw(c);
            drawable = new BitmapDrawable(resources, b);
        }
        return drawable;
    }
}
GregoryK
  • 3,011
  • 1
  • 27
  • 26
  • What is @AnimatorSetter? – Steve M Nov 26 '16 at 17:18
  • Oops... It's just my custom annotation. I use it to indicate to ProGuard and Android studio that this method is actually used for animation. (ObjectAnimator.ofFloat(this, "rotation", 0, 0); invokes setRotation during execution) – GregoryK Nov 27 '16 at 06:58
  • This class needs to be kept in ProGuard and VectorDrawable will cause crash in pre Lollipop, but don't need it unless you using vector drawables. – Steve M Nov 28 '16 at 00:19
  • @SteveM you can use the following check to support pre Lollipop (drawable instanceof VectorDrawableCompat || drawable.getClass().getSimpleName().equals("VectorDrawable")) insteado (drawable instanceof VectorDrawable) – GregoryK Nov 28 '16 at 09:48