33

The two states of the image

What I Have

I have an arrow image (like the left one). When the user clicks on it, it should rotate 180 degree with an animation and should look like the right one.

What I Have Done

private void rotate(float degree, final int toggleV) {

        final RotateAnimation rotateAnim = new RotateAnimation(0.0f, degree,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f,
                RotateAnimation.RELATIVE_TO_SELF, 0.5f);

        rotateAnim.setDuration(500);
        toggle.startAnimation(rotateAnim);
        rotateAnim.setAnimationListener(new Animation.AnimationListener() {

            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {


                if (toggleV == 1)
                    toggle.setImageResource(R.drawable.toggle_up);
                else
                    toggle.setImageResource(R.drawable.toggle_down);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
    }

The Problem

I see that the animation works fine but there is a little flicker while setting the image. May be because of the time difference when the animation ends and the image is set.

How can I remove this flicker issue? Do you have any better approach to do this?

Aritra Roy
  • 15,355
  • 10
  • 73
  • 107
  • Rotate animation programmatically. [RotateAnimation : clockwise/anticlockwise](https://stackoverflow.com/a/52983423/5872337) – Geet Thakur Oct 25 '18 at 07:20

9 Answers9

75

First of all, what is you minimum SDK requirement? In case it's at least Android 3.0, you can use the newer animation framework, and animate your Image with something like this:

imageView.animate().rotation(180).start();

About the flicker: I wouldn't reset the source image of the ImageView after the rotation, I'd just leave in the original and make sure that the rotation animation fills after the animation, leaving the image rotated. The flicker is most likely caused by the View's relayout/redraw upon changing the source image.

Further visual artifacts (flicker?) may be caused because the original-rotated image and the rotated static image might differ in a few pixels.

Zsombor Erdődy-Nagy
  • 16,864
  • 16
  • 76
  • 101
  • 2
    The ImageView just rotates once when I press it. But it should rotate again after I press it again and so on. But this doesn't happen. Any idea? – Aritra Roy May 13 '15 at 12:33
  • 10
    Well, in case you are using the code example that I provided, this code specifically tells to view to "rotate TO 180 degrees". So if it's already rotated to 180 degrees, then it will do nothing. In the click listener you could e.g. check to current rotation of the View (.getRotation), and if it's not 0, then rotate it back to 0 in a similar way. – Zsombor Erdődy-Nagy May 13 '15 at 14:34
  • Yes, I just did that and it worked too. Now, is there a way to control the animation direction when rotating to 0. I mean, it is rotating in a particular direction but I want to go in the opposite way. – Aritra Roy May 13 '15 at 16:07
  • I guess you want to continue rotating the view in the same direction instead of rotating CW to 180 and CCW back to 0. You could e.g. set and ever increasing rotation target that is X * 180 degrees, based on what rotation the View has when the user interacts (which could happen mid-animation). E.g. in case we need to rotate for clicks on the View: Click -> rotate to 180 -> Click -> Rotate to 2 x 180 -> ... you get the idea. – Zsombor Erdődy-Nagy May 13 '15 at 21:59
  • Use LinearInterpolator to get full turn – Andrew Glukhoff Nov 18 '19 at 08:08
  • 1
    Go: `imageView.animate().rotation(0).rotation(180).start();` Back: `imageView.animate().rotation(180).rotation(0).start();` – Sam Chen May 19 '20 at 21:16
4

If I were you I'd use ViewPropertyAnimator (available from API 12). Its syntax is more straight forward IMO.
Usage would be:

toggle.animate().rotation(0.5f);
Alex.F
  • 5,648
  • 3
  • 39
  • 63
2

You can use setFillAfter if you want to persist the state of your animation.

Rajesh
  • 15,724
  • 7
  • 46
  • 95
2

Why dont you use RotateAnimation?

create a folder named anim in res and a file named rotator.xml inside res/anim.

<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="400"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="360"/>

Here one complete rotation will be completed in 4000ms (4 seconds). Now add a PNG image that you want to rotate into your drawable folder. Then open res/main.xml, after removing the default textView in the layout, add an ImageView and Button into the layout. Set the src property of the ImageView as your filename of the added image, for example android:src=”@drawable/myimg” Ok, lets edit the main class. In the onClick() for the button, add the necessary code for running the animation. Check the following code.

public class AnimationActivity extends Activity {

    public ImageView  myImage ;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        myImage = (ImageView)findViewById(R.id.imageView1);
        final Animation myRotation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.rotator);
        ((Button)findViewById(R.id.button1)).setOnClickListener(new OnClickListener()    {
            @Override
            public void onClick(View arg0) {
                myImage.startAnimation(myRotation);
            }
        });
    }
}
Komal12
  • 3,340
  • 4
  • 16
  • 25
Vinay Jayaram
  • 1,030
  • 9
  • 29
2

Verified Code: (You can follow my solution)

imageView.setImageResource(R.drawable.ic_arrow_up);

boolean up = true;

if (!up) { 
    up = true; 
    imageView.startAnimation(animate(up)); 
} else { 
    up = false; 
    imageView.startAnimation(animate(up)); 
}

private Animation animate(boolean up) {
    Animation anim = AnimationUtils.loadAnimation(this, up ? R.anim.rotate_up : R.anim.rotate_down);
    anim.setInterpolator(new LinearInterpolator()); // for smooth animation
    return anim;
}

drawable/ic_arrow_up.xml

<vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="24dp"
        android:height="24dp"
        android:viewportWidth="24.0"
        android:viewportHeight="24.0">
    <path
        android:fillColor="#3d3d3d"
        android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
</vector>

anim/rotate_up.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:fillEnabled="true">
    <rotate
        android:duration="200"
        android:fromDegrees="-180"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="0" />
</set>

anim/rotate_down.xml

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:fillEnabled="true">
    <rotate
        android:duration="200"
        android:fromDegrees="0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:toDegrees="180" />
</set>

I used that code. Because this will save the animation state:

android:fillAfter="true"
android:fillEnabled="true"
Ahamadullah Saikat
  • 4,437
  • 42
  • 39
1

If you want to rotate an image by 180 degrees clockwise.

private var isExpanded = true

private fun rotateImage(view: View) {
    val startAngle = if (isExpanded) 0f else 180f
    ObjectAnimator.ofFloat(view, View.ROTATION, startAngle, startAngle + 180f).apply {
        duration = 300
        interpolator = LinearInterpolator()
        start()
    }
    isExpanded = !isExpanded
}

Or more simply (as wrote @Alex.F):

view.animate().setDuration(300).rotationBy(180f).start()

Note that if you rotate the image many times too frequently, it doesn't stop in 0 - 180 - 360 positions. Because if you start a new animation before a previous finished, it will shift an angle.

So, a better way is written in the accepted answer. It doesn't depend on current animation state.

private var angle = 0f

angle += 180f
view.animate().setDuration(300).rotation(angle).start()
CoolMind
  • 26,736
  • 15
  • 188
  • 224
1
img_view.animate().rotation(360.0f).setDuration(1000);
Tohid Zamani
  • 84
  • 2
  • 10
1

KOTLIN

very efficient and smooth ImageView rotation animation in Kotlin.

After the rotation animation, the ImageView is retained without the need for an animation listener.

imageView.animate().rotation(angle).setDuration(700).setInterpolator(AccelerateDecelerateInterpolator()).start()
floki1
  • 106
  • 2
  • 5
0

KOTLIN:

A smooth way to make an expand and collapse animation with only one ImageView:

class MainActivity : AppCompatActivity() {

    var isExpanded = true

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)


        val imageView = findViewById<ImageView>(R.id.image_header_toggle)

        imageView.setOnClickListener {
            if (!isExpanded) {
                imageView.animate().apply {
                duration = 500
                rotation(180f)
                isExpanded = true}
            }else{
                imageView.animate().apply {
                duration = 500
                rotation(0f)
                isExpanded = false}
            }
        }
    }
}
Ole Pannier
  • 3,208
  • 9
  • 22
  • 33