So, my question is somewhat similar to these two questions:
Custom View onDraw is constantly called
android: onDraw is called constantly
I have a custom class extending ImageView which I apply a RotateAnimation to. The animation uses input x- and y-coordinates to perform a rotation from the last angle to the next so the user can turn the ImageView as wanted, from -360 degrees to 360 degrees. When I use this code for onDraw() everything looks perfect on screen (the animation is set as in the code further below):
@Override
protected void onDraw(Canvas canvas) {
Log.d(TAG, "It is drawn again!");
this.setAnimation(anim);
super.onDraw(canvas);
}
The problem with this is that, in the same manner as in the other posts mentioned, the animation calls onDraw which calls animation and so forth, probably through invalidate() in the RotateAnimation class. Is this correctly observed? The output is perfect since the ImageView always stays at the current calculated angle, but the animation calulations thus keep on going, consuming lots of power and capacity.
To fix this I tried to move the this.setAnimation(anim) in the method where the parameters for the animation are calculated (please ignore isClockWise(), calculateAngleToMove() and other non-android stuff, they work as intended):
private void turnWheel(){
float angle = 0;
if ( isClockWise() ){
angle = calculateAngleToMove();
anim = new RotateAnimation(current_angle, angle, center_x, center_y);
anim.setFillAfter(true);
anim.setFillEnabled(true);
current_angle += angle;
}
else{
angle = - calculateAngleToMove();
anim = new RotateAnimation(current_angle, angle, center_x, center_y);
anim.setFillAfter(true);
anim.setFillEnabled(true);
current_angle += angle;
}
if ( current_angle > 360 ){
current_angle = current_angle - 360;
}
if ( current_angle < -360 ){
current_angle = current_angle + 360;
}
this.setAnimation(anim);
this.invalidate(); //Calls onDraw()
}
This solves the problem with onDraw constantly being called, but it creates another: when the user pushes, holds and turns the ImageView, it snaps back and forth between zero angle and the current angle. When the user then lets go of the ImageView, it snaps back to zero angle. It is wanted to always have the ImageView rotated at the variable current_angle, also when the user doesn’t provide input.
I have tried different versions of anim.setFillAfter(true), anim.setFillEnabled(true), invalidate() and this.startAnimation(anim), but they never seem to have any effect on this problem.
Where is the best place to call this.setAnimation(anim)?
I might recommend not using the
Animationframework to apply static transformations to views so that you don’t have to deal with the issues caused by resetting when you apply a newAnimationeach time.If the contents you wish to rotate just includes imagery, consider wrapping your image(s) in a
RotateDrawableand set that as the content of yourImageView. With this you can control how the drawable rotates by setting its level value (eitherDrawable.setLevel()orImageView.setImageLevel()should do the trick) and the transformation will stick. If you have a need to automate the animation for a period, you can simply call the level setting code inside of aHandlerthat posts the updates every so often.Another option would be to create a custom
ViewGroupand use thegetChildStaticTransformation()method to apply the rotation to any child view (be sure to enable it or override a subclass that already has it enabled).Transformationis what theAnimationframework uses to modify view appearance as well. In this case, you would likely still need to callinvalidate()when the user input changed in order to force the re-draw. The sameHandlerrule would apply if you wanted to automate the changes periodically.RotateDrawable Example
Let’s say the image you are wanting to rotate is at res/drawable/wheel.png, create a simple XML file at res/drawable/wheel_rotate.xml:
To wrap your image into a
RotateDrawable. Then, in your Java code set that drawable as the content of anImageView:By default, a level ranges from 0 to 10,000. So that maps to 0 level = fromDegrees and 10,000 level = toDegrees.
HTH