I am using RotateAnimation to spin an ImageView to a certain degree (my ImageView is a circle).
degree_old = degree % 360;
degree = random_sector.nextInt(3600) + 720;
This is the code I use for the first spin:
final RotateAnimation rotate = new RotateAnimation(0, degree, RotateAnimation.RELATIVE_TO_SELF,
0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(5000);
rotate.setFillAfter(true);
rotate.setRepeatCount(0);
rotate.setRepeatMode(0);
rotate.setInterpolator(new DecelerateInterpolator());
And this code to spin to the start position again:
final RotateAnimation rotateBack = new RotateAnimation(degree, (back_degree + degree), RotateAnimation.RELATIVE_TO_SELF,
0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateBack.setDuration(3000);
rotateBack.setFillAfter(true);
rotateBack.setRepeatCount(0);
rotateBack.setRepeatMode(0);
rotateBack.setInterpolator(new LinearInterpolator());
It works.
My problem is that the speed rotation isn't the same every time. Sometimes it spins with a very high velocity and other time with a very low velocity.
My question is: Is there a way to have every time the same velocity? I prefer my animation to spin with a low velocity for 5 seconds.
How can I change the the spinning speed?
Not sure if you are wanting to use only RotateAnimation.
The following code will rotate an Image 360 degrees and back again very smoothly.
Obviously you can adjust this code to include your random generator.
public class MainActivity extends AppCompatActivity {
ImageView my_image;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
my_image = findViewById(R.id.my_image);
animate_it();
}
private void animate_it() {
my_image.setRotation(0);
my_image.animate().rotation(360).setDuration(5000).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
my_image.animate().rotation(0).setDuration(5000);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
}
Related
I have a question of RotationAnimation.
First.
Is there a Listener to during animation?
start, start animation
repeat, repeat animation
stop, stop animation.
but there are no animation listener to check the on going.
second,
is any other get a current rotate angle of image?
I think, ImageView is rotate by rotationAnimation function.
so I made a timer thread and run 1second
'''
timer = new Timer();
timerTask = new TimerTask() {
public void run(){
Log.e("LOG", " [angle]: " + String.format("%3.1f", rotateImage.getRotation());
}
};
timer.schedule(timerTask, 0, 1000);
'''
but, I can't see the changed value during rotate.
how can I get the current angle during rotate?
thanks.
For Rotation Animation, Yes There is as shown below:
RotateAnimation rotateAnimation = new RotateAnimation();
rotateAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
You can also use Object Animators to animate Rotation:
ObjectAnimator rotateAnimation = ObjectAnimator.ofFloat(targetView, View.ROTATION, startAngle, endAngle);
rotateAnimation.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
To get the angle of your ImageView simply use imageView.getRotation(); this will give you an int value of the current angle of rotation.
You also don't need Timer because both ObjectAnimator and rotateAnimator provide time control for you:
rotateAnimation.setDuration(1000); // run animation for 1000 milliseconds or 1 second
rotateAnimation.setStartDelay(1000); // delay animation for 1000 milliseconds or 1 second
Finally, To get rotation Angle DURING the time animation is running there is a listener method called addUpdateListener :
rotateAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue(); // dynamic value of angle
}
});
I have two things.
The first is a looping scale animation doing a kind of permanent zoom in / zoom out.
The second thing is a TimerTask that set the duration of this scale animation every 20 seconds.
The problem is that sometimes there is kind of "jump" in the animation when the setDuration() occurs.
First i put this setDuration() in the TimerTask, then I just tried to put a flag in the TimerTask and changed the duration in onAnimationEnd(), it didn't work neither, same problem. In he code below I use this flag technic.
In case it's not enough clear the goal of all this is to have an "infinite" zoom in / out of a drawable circle, the zoom in / out speed decreasing in time. It actually works but it's not smooth, as said above.
Is there a way to do this smoothly ?
The TimeTask that sets the flag "changeDurationFlag"
private void setRegularRythmeDecrease() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask task = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
if (elapsedTime > sessionLengthInSec) {
circle.clearAnimation();
}
zoomDuration = zoomDuration + (toDecreaseEveryUpdate / 2);
changeDurationFlag = true;
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
};
timer.schedule(task, 0, BREATH_RYTHME_UPDATE_INTERVAL_IN_SECONDS*1000);
}
The ScaleAnimation I use to zoom in and out
public Animation scaleAnimation(View v, float startScale, float endScale, long duration) {
Animation anim = new ScaleAnimation(
startScale, endScale,
startScale, endScale,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
anim.setFillAfter(true);
anim.setDuration(duration);
return anim;
}
The Animation listeners where the duration is set
zoomDuration = ZOOM_DURATION_START;
animZoomIn = scaleAnimation(circle, 1f, ZOOM_FACTOR,zoomDuration);
animZoomOut = scaleAnimation(circle, ZOOM_FACTOR, 1f,zoomDuration);
animZoomIn.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// If the flag is true (modified in the TimerTask) I set the Duration to decrease the speed
// it's where the not smoothly thing happens
if(changeDurationFlag) {
Log.d("beat ","Set breath to " + String.valueOf(zoomDuration * 2d));
animZoomIn.setDuration(zoomDuration);
animZoomOut.setDuration(zoomDuration);
changeDurationFlag = false;
}
circle.startAnimation(animZoomOut);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
animZoomOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
circle.startAnimation(animZoomIn);
currentDateTime = Calendar.getInstance().getTime();
elapsedTime = currentDateTime.getTime() - startDateTime.getTime();
long elapsedTimeInSeconds = TimeUnit.MILLISECONDS.toSeconds(elapsedTime);
beatCount++;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
I had a problem with the value update of the variables elapsedTime and sessionLengthInSec. It means that sometimes (elapsedTime > sessionLengthInSec) was true when it shouldn't and a circle.clearAnimation() happened... so sure it produced a jump !
Sure the jump can still happen if the duration change is too much big, but it's kind of normal since the speed would increase rapidly. In my code it was not the case because the speed increasing is less than 200ms so hardly viewable with eyes/
Sorry for this, actually this code works perfectly...
I'm implementing animation when slide between fragments in ViewPager.
when slide to new fragment I want to scale the view and the make start a set of animation includes alpha and translate animation but when start the set problem is scale animation's result was not keeping (the view return to the original size).
I had tried to with
scaleAnimation.setFillAfter(true);
scaleAnimation.setFillEnabled(true);
But it just work when I do not start set of animation above, When I start the set, the view is return to original size.
Any body can give me a solution?
EDIT:
First thing I want to scale an imageView by animation:
ScaleAnimation scaleAnimation = new ScaleAnimation(1f, 1.3f, 1f, 1.3f, Animation.RELATIVE_TO_SELF, 0f, Animation.RELATIVE_TO_SELF, 1);
scaleAnimation.setDuration(1000);
scaleAnimation.setFillAfter(true);
scaleAnimation.setFillEnabled(true);
Then I want after scale, ImageView still keep new scale size and start another Animation as:
private void startSetAnimation() {
AnimationSet set = new AnimationSet(true);
set.setDuration(3000);
set.setFillAfter(true);
float alphaS = 1f;
AlphaAnimation alpha = new AlphaAnimation(currentAlpha, alphaS);
alpha.setDuration(3000);
alpha.setFillAfter(true);
float scale = 0.1f;
TranslateAnimation translate = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, currentTrans,
Animation.RELATIVE_TO_SELF, scale,
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF , 0f);
translate.setFillAfter(true);
translate.setDuration(3000);
set.addAnimation(translate);
set.addAnimation(alpha);
ivHorizontal.startAnimation(set);
}
So I add below code to scale animation to make it but not success.
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
startSetAnimation();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
ivHorizontal.startAnimation(scaleAnimation);
There is an onAnimationEnd listener you can utilize kick off certain animations when other ones are done. I'll post code when I get to my computer
Edit:
The reason why your imageView doesn't retain its new size after scaling is because Animations aren't permanent, what you need to do is this
scaleAnimation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
// change the actual width and height so it doesn't go back to what it was
LayoutParams layoutParams = (LayoutParmas) ivHorizontal.getLayoutParams();
layoutParams.width = ivHorizental.getWidth();
layoutParams.height= ivHorizental.getHeight();
ivHorizontal.setLayoutParams(layoutParams);
ivHorizontal.requestLayout();
startSetAnimation();
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
ivHorizontal.startAnimation(scaleAnimation);
If I create a RotateAnimation based on certain duration and degree parameters, but I want to call the same animation with different duration and degree parameters, how do I do that?
RotateAnimation animation = new RotateAnimation(fromDegrees, toDegrees, width, height/2);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(animationDuration);
animation.setFillAfter(true);
animation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
activityTimeOrRepeat.setText(task.getDurationTitle());
activityTimeOrRepeat.setTextColor(textColor);
activityDescription.setText(task.getTitle());
activityDescription.setTextColor(textColor);
timeLeftForThisActivity.setTextColor(textColor);
currentTimerIndex[0] = index;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
timeLeftForThisActivity.setText("00:00");
CountDownTimer currentTimer = timers[index];
currentTimer.cancel();
if (index == taskSize - 1) {
overallTimer[0].cancel();
startButton.setBackgroundDrawable(context.getResources().getDrawable(R.drawable.done_button_master));
startButton.setTag(context.getString(R.string.wtrDoneButtonTag));
elapsedTimeDynamic.setText(String.format("%02d:%02d", (totalActivityTime / 1000) / 60, (totalActivityTime / 1000) % 60));
}
else {
RotateAnimation nextAnimation = animations[index + 1];
CountDownTimer nextTimer = timers[index + 1];
nextTimer.start();
workoutWidget.startAnimation(nextAnimation);
}
}
});
How do I call this same animation, but with different duration and degree parameters?
Hi I am applying multiple animations to a view using AnimatorSet and ValueAnimator.
When the user touches the view, successive animations are applied to the view. I do this to have a "zooming effect" when the user is moving his fingers on the view.
The view is a custom gallery.
Here is the code for animation:
private void createAnim(final View v) {
animating = true;
if (D)
Log.i(TAG, "sarting animation");
try {
v.clearAnimation();
v.getAnimation().reset();
} catch (Exception e) {
}
set = new AnimatorSet();
set.addListener(new AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
if (D)
Log.i(TAG, "Animation ended");
animating = false;
}
#Override
public void onAnimationCancel(Animator animation) {
animating = false;
if (D)
Log.i(TAG, "Animation cancelled");
}
});
set.setDuration(100);
ValueAnimator v1 = ObjectAnimator.ofFloat(v, "scaleX", mScaleFactor);
ValueAnimator v2 = ObjectAnimator.ofFloat(v, "scaleY", mScaleFactor);
animationList.add(v1);
animationList.add(v2);
set.playTogether(v1, v2);
set.start()
}
I have an onTouchListener and on action move I successively call this method increasing or decreasing the mScaleFactor.
When the user releases the finger I would like the view to go back to previous state, I mean to erase all animations applied as if they were never applied to view. The problem is you can easily do that for one animation but for many like that I just can't find the right way. Here is what I have tried:
-Adding the animations to an ArrayList and doing animation.reverse() on each one of them with a delay of +100 added to each of them so the execute one after another but the end result just doesn't seem exactly the same as before animations.
v.clearAnimation(); only cancels the last animation
v.getAnimation().reset(); same only works for last/active animation.
Is there a method to just get the view back as it was before any animation has been started?
Thanks a lot
Reversing the effects of an AnimatorSet can be tricky simply because there is no "reverse" method. The easiest method in this case, would probably simply be another AnimatorSet that does the opposite of the original. The issue here would be is your AnimatorListener could be out of sync. If you do this way and you want to keep the boolean, then you'd have to implement it as somewhat of a semaphore.
public static class AnimatorTracker implements AnimatorListener{
int counter;
public AnimatorTracker() {
counter = 0;
}
public boolean isAnimating() {
return counter == 0;
}
#Override
public void onAnimationStart(Animator animation) {
counter++;
}
#Override
public void onAnimationRepeat(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
counter--;
}
#Override
public void onAnimationCancel(Animator animation) {
// Canceling an animation invokes onAnimationEnd, so nothing needs to be done.
}
}
Then just make the sets the same way you just did:
AnimatorTracker tracker = new AnimatorTracker(); // or make it a global reference
AnimatorSet set = new AnimatorSet();
AnimatorSet reverseSet = new AnimatorSet();
ValueAnimator v1 = ObjectAnimator.ofFloat(v, "scaleX", mScaleFactor);
ValueAnimator v2 = ObjectAnimator.ofFloat(v, "scaleY", mScaleFactor);
ValueAnimator reverseV1 = ObjectAnimator.ofFloat(v, "scaleX", 1.0f);
ValueAnimator reverseV2 = ObjectAnimator.ofFloat(v, "scaleY", 1.0f);
set.addListener(tracker);
set.setDuration(100);
set.playTogether(v1, v2);
reverseSet.addListener(tracker);
reverseSet.setDuration(100);
reverseSet.playTogether(reverseV1, reverseV2);
Call set.start() when you want to animate forward. Call reverseSet.start() when you want to animate back.