var anim = AnimatorSet()
var anim1 = AnimatorSet()
anim1.playSequentially(mutablelist)
anim.play(anim1)
var anim2 = AnimatorSet()
anim2.playTogether(mutablelist)
anim.play(anim2)
anim.start()
Like this I have So many animator inside the Base Animator set.Now My question is ,I want to play the ( anim ) sequentially. But here it runs parallely.Kindly suggest good way to slove this.
There are two different way to adding animations to an AnimatorSet: either the AnimatorSet#playTogether(Animator[]) or AnimatorSet#playSequentially(Animator[]) this methods can called to add a set of animations all at once, or the AnimatorSet#play(Animator) can used in conjunction with methods in the AnimatorSet.Builder class to add animations one by one.
It is possible to set up AnimatorSet with circular dependencies between its animations. For example, an animation a1 could be set up to start before animation a2, a2 before a3, and a3 before a1.
more information about it click hear
Related
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.
Is there a way to run an AnimatorSet in reverse on Android? The ValueAnimator API does provide a reverse method on the individual animators but not on a set of animators.
If your AnimatorSet is being played sequentially then you could use the method mentioned by #blackbelt:
public static AnimatorSet reverseSequentialAnimatorSet(AnimatorSet animatorSet) {
ArrayList<Animator> animators = animatorSet.getChildAnimations();
Collections.reverse(animators);
AnimatorSet reversedAnimatorSet = new AnimatorSet();
reversedAnimatorSet.playSequentially(animators);
reversedAnimatorSet.setDuration(animatorSet.getDuration());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
// getInterpolator() requires API 18
reversedAnimatorSet.setInterpolator(animatorSet.getInterpolator());
}
return reversedAnimatorSet;
}
The caveat being that this only works for simple sequential animations as any dependencies setup in the original AnimatorSet will be lost. Also, if an interpolator was used on the AnimatorSet it will only carry over on API 18 or newer (per method mentioned above, you could alternatively manually add the interpolator back to the new reversed animator set).
The individual animations within the AnimatorSet will not play in reverse, if that is desirable then you'll also have to iterate over the animations of the AnimatorSet and set a ReverseInterpolator on each, see answer to Android: Reversing an Animation.
You can take the initial input values from i.e ValueAnimator.ofFloat(0, 1) and switch them around with yourAnimator.setFloatValues(1, 0) before calling yourAnimatorSet.start() when you want to reverse animation
The reverse() method has been added in API 26:
Plays the AnimatorSet in reverse. If the animation has been seeked to a specific play time using setCurrentPlayTime(long), it will play backwards from the point seeked when reverse was called. Otherwise, then it will start from the end and play backwards. This behavior is only set for the current animation; future playing of the animation will use the default behavior of playing forward.
Note: reverse is not supported for infinite AnimatorSet.
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();
}
}
I am trying to set blink animation on two words so that they blink one after the other, but what ever I do only 2nd word is displayed, can any one provide me method for doing the same, I am working with API level 10 so, cannot use "Animatorset".
AnimationSet set = new AnimationSet( true );
Animation blink = new AlphaAnimation(1, 0 );
blink.setDuration(duration);
blink.setFillAfter(true);
set.addAnimation( blink );
txtvw.setText("FIRST");
txtvw.setVisibility(View.VISIBLE);
txtvw.setAnimation(blink);
AnimationSet set2 = new AnimationSet( true );
Animation blink2 = new AlphaAnimation(1, 0 );
blink2.setDuration(duration);
blink2.setFillAfter(true);
set2.addAnimation( blink );
txtvw.setText("SECOND");
txtvw.setVisibility(View.VISIBLE);
txtvw.setAnimation(blink2);
if your code is like this, your text "first" gets replaced by text "Second" immediately. And the animation is shown. This animation gives an illusion that only second is blinking. Your text first is being set for microseconds but it immediately gets replaced by second.
If you want to show both the texts, you may need to use Thread
Using two animation sets for the same animations (that is, "blinking animation") is an overkill. You may just repeat your animation 2 times. Use setRepeatCount(int) in conjunction with setRepeatMode(int) to achieve this.
If you insist on using the code you have provided in your post, you will need to specify animation offset. Animation offset indicates "how much time should animation wait before start". For example, if you have Animation a and b, and want b happen after a, you might use the code:
b.setStartOffset(a.getDuration());
Then b animation will happen just after a has finished.
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);
}
});