I use the ObjectAnimator API (android.animation.ObjectAnimator) to animate a button once it's clicked (v is the Button):
ObjectAnimator animator = ObjectAnimator.ofFloat(v, "rotationY", 360f);
animator.setDuration(5000);
animator.start();
When I test this on the emulator, it works for the first click (button rotates). But when I click the button again (the fragment is not destroyed etc. after the first click), I don't see any animation on the emulator (the emulator isn't the fastest, but with 5 seconds I should see something).
Do I need to destroy/close something after the first animation or what am I missing?
Does anyone have a hint or can reproduce this?
Thanks in advance,
Martin
The second time you will try to animate from 360.0f to 360.0f. Change your call to ofFloat() to:
ObjectAnimator.ofFloat(v, "rotationY", 0.0f, 360.0f)
To elaborate on Romain's answer, the result of the single-value factory constructor is animation that will run from whatever the current value is to the value specified in the parameters. In your case, the object had a value of 0 to begin with an animated (the first time) to a value of 360. The second time it ran, it animated from the current value (360) to the specified value (360). Not much of an animation.
The fix is as above: hard-code both the start and end values for the animator. Alternatively, you can reset the value back to 0 when the animation finishes by implementing the AnimatorListener.onAnimationEnd method and resetting it when the animation finishes:
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
v.setRotationY(0);
}
});
Related
Building this app, I have managed to use some animations
using them with View view.setAnimation() etc..
This is the code i have:
// animation Properties
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator()); // add this
fadeIn.setDuration(5000);
AnimationSet animation1 = new AnimationSet(false); // change to false
//animation.addAnimation(fadeIn);
animation1.addAnimation(fadeIn);
animation1.setRepeatCount(1);
Animation fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator()); // and this
//fadeOut.setStartOffset(fadeInDuration + timeBetween);
fadeOut.setDuration(5000);
AnimationSet animation = new AnimationSet(false); // change to false
//animation.addAnimation(fadeIn);
animation.addAnimation(fadeOut);
animation.setRepeatCount(1);
textViewTopBannerBizName.setAnimation(animation1);
textViewTopBannerBizCategory.setAnimation(animation1);
So all, I want is that textViewTopBannerBizName and textViewTopBannerBizCategory will fade into screen as i used animation1 for both of them.
However first time when i launched the app it worked perfect but when I relaunched it again it stopped working.
It makes me wonder... why...?
Please help,
Thank you for your time.
try this.(Tested)
textViewTopBannerBizName.startAnimation(animation1);
You can clear animation if cached before by calling clearAnimation() and then startAnimation or setAnimation.
Edited
setAnimation
Sets the next animation to play for this view.But view animation does not start yet.
startAnimation
If you want the animation to play immediately, use startAnimation. This method provides allows fine-grained control over the start time and invalidation, but you must make sure that
1) the animation has a start time set,
2) the view will be invalidated when the animation is supposed to start
After setting animation, Please do invalidate the view if it's not an Activity
imb6.setAnimation(MainActivity.blinkAnimation(mContext, true))
invalidate();
I wish this'll helps.
Been doing some animation inside of a row in RecyclerView (not the row itself. Imagine expanding text) and there are times when the animation leaks to other recycled views which should not have that animation in them.
Since I use property animation the scale action resizes the inner view and the leak can be seen in 2 aspects:
1) The animation will go on (This I could overcome with some guards)
2) The view has been resized and stopped in its tracks and so it will be reflected in the recycled view.
How can I reset the views to their original state? I have tried many approaches from posts but none solved it. The closest definition I got was in this unanswered post:
How to reset view to original state after using animators to animates its some properties?
Here is a sample of how I set up my animation in onBind (this one has an attempt to use onAnimationEnd which I found in one post but did not work)
ObjectAnimator scaleXUp = ObjectAnimator.ofFloat(mView, View.SCALE_X, 10f);
scaleXUp.setRepeatCount(ValueAnimator.INFINITE);
scaleXUp.setRepeatMode(ValueAnimator.REVERSE);
scaleXUp.setDuration(700);
ObjectAnimator scaleYUp = ObjectAnimator.ofFloat(mView, View.SCALE_Y, 10f);
scaleYUp.setRepeatCount(ValueAnimator.INFINITE);
scaleYUp.setRepeatMode(ValueAnimator.REVERSE);
scaleYUp.setDuration(700);
mTotalAnimation = new AnimatorSet();
mTotalAnimation.play(scaleXUp).with(scaleYUp);
mTotalAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
mTotalAnimation.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
animation.removeListener(this);
animation.setDuration(0);
for(Animator va : ((AnimatorSet)animation).getChildAnimations()) {
((ValueAnimator)va).reverse();
}
}
});
mTotalAnimation.start();
And here is what I do in the onUnbindData:
if (mTotalAnimation != null) {
mTotalAnimation.end();
mTotalAnimation = null;
}
And as I saw many people like the clearAnimation approach - tried and did not work either.
4 days and not one response but meanwhile I solved it myself.
So the approach was close, just wrongly placed.
I added this method:
private void stopAnimation() {
for (Animator anim : mTotalAnimation.getChildAnimations()) {
((ObjectAnimator) anim).reverse();
anim.end();
}
}
and I call that when I want to reset the view.
Here I am getting the animations from the AnimatorSet and reversing and ending each. I did not understand why I had to do it manually but it looks like this ability will be added in Android O:
https://developer.android.com/preview/api-overview.html#aset
Starting in Android O, the AnimatorSet API now supports seeking and playing in reverse. Seeking lets you set the position of the animation set to a specific point in time. Playing in reverse is useful if your app includes animations for actions that can be undone. Instead of defining two separate animation sets, you can play the same one in reverse.
I've got an animation to perform which consists of some arrow heads aligned horizontally where the alpha values of the arrows will change to achieve an animation effect (i.e. first arrow has alpha 1.0, then the second will get a value 1.0 etc.).
So if I have a function like this:
void highlightFirstArrow()
{
mArrow1.setAlpha(1.0f);
mArrow2.setAlpha(0.75f);
mArrow3.setAlpha(0.50f);
mArrow4.setAlpha(0.20f);
}
Then I'd want to start, repeat numerous times, then stop a function such as this:
void animateArrows()
{
highlightFirstArray();
pause;
highlightSecondArray();
pause;
etc.
}
Obviously this would lock up the GUI thread if it were performed in a for look for example. What are the options for achieving the desired animiation:
- run a for loop in a separate thread
- don't use a loop, instead constantly execute the functions individually via a timer
- use built in specific android animation mechanisms. If so which is most appropriate? Would AnimatorSet() be good for this scenario, or something else
You definitely shouldn't use any loops or timers. There're lots of built in classes which could help to animate your views. For instance you can use ValueAnimator:
ValueAnimator.ofFloat(1f, 0.2f).setDuration(1000).addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
arrow1.setAlpha(value);
arrow2.setAlpha(value);
}
});
I have a object animator started, and i want to change the end value sometimes when the animation is started, but without stoping and restarting it.
How can this be achieved? I can't find any info in google.
SAMPLE:
XMovement = ObjectAnimator.ofFloat(view, "x", startX, endX);
XMovement.setDuration(ANIMATION_TIME);
XMovement.start();
and i want to change endX value in real time when the animation is started.
Thanks
According to the documentation http://developer.android.com/reference/android/animation/AnimatorSet.html#end() end() method should assign the end values for all animations inside the set. However ending does nothing if the set was not started. This is different from ValueAnimator i.e. which does not care and artifically starts the animation, fires onAnimationStart() event and then ends it which results in setting the end values properly.
Let me tell you why this is a problem. If you want to have the ability to update your View with and without animation (depending on the situation) you don't want to have two separate methods for it. Instead you could create an animation and start it normally or immediately set the end values. With ValueAnimator this works nicely. However AnimatorSet does not play ball and if you try to end it before calling start() it simply exits.
One workaround for this is to call start() and end() immediately after but it does not cover the situation with nested AnimatorSets like this:
AnimatorSet mainSet = new AnimatorSet();
AnimatorSet scaleSet = new AnimatorSet();
scaleSet.playTogether(
ObjectAnimator.ofFloat(view, "scaleX", 0.5f),
ObjectAnimator.ofFloat(view, "scaleY", 0.5f));
mainSet.playSequentially(
ObjectAnimator.ofFloat(view, "alpha", 0.5f),
scaleSet);
mainSet.start();
mainSet.end();
This causes the mainSet to call end() on all its children. But since AnimatorSet.end() is broken the scaleSet does not set ending values. I attempted to extend AnimatorSet class and fix this but soon found out it is marked final.
In my app I mostly use animations but have some few cases where animations are not needed. Does anyone have an idea how to achieve the proper behaviour?
You can do that on all child animations with:
for (Animator animation : mainSet.getChildAnimations())
{
animation.start();
animation.end();
}
I know this is an old question but a recursive approach can solve this:
void endAnimationsRecursively(final Animator mainAnimator){
if (mainAnimator instanceof AnimatorSet) {
for (final Animator animator : ((AnimatorSet)
mainAnimator).getChildAnimations()) {
endAnimationsRecursively(animator);
}
mainAnimator.end();
mainAnimator.cancel();
}
}