I have the following code which splits an image in half and animate each part in different direction:
final AnimatorSet mSetAnim = new AnimatorSet();
final Animator topAnim = ObjectAnimator.ofFloat(topImage, "translationY", (bmp.getHeight() / 2) * -1);
final Animator bottomAnim = ObjectAnimator.ofFloat(bottomImage, "translationY", bmp.getHeight() / 2);
mSetAnim.setDuration(duration);
mSetAnim.playTogether(topAnim, bottomAnim);
mSetAnim.start();
I'm using NineOldAndroids in order to support old android versions but for some reason no matter what value i put in 'duration' it doesnt seem to have any affect. The animation duration stays the same - half a second more or less.
When i use android's animation apis i can see that the duration is changing.
Any idea how to workarround this ?
Using mSetAnim.getDuration(), Try printing in a toast to see what value exists at runtime.
Use mSetAnim.setDuration(duration); after playTogether() as is done in nineoldandroids.com.
Related
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.
Was trying to implement the snippet below. [from developer documentation here]:
View myView = findViewById(R.id.my_view);
// get the center for the clipping circle
int cx = (myView.getLeft() + myView.getRight()) / 2;
int cy = (myView.getTop() + myView.getBottom()) / 2;
// get the final radius for the clipping circle
int finalRadius = Math.max(myView.getWidth(), myView.getHeight());
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();
Gettting the error below. Why is it so and how can i fix this.
java.lang.NoClassDefFoundError: android.view.ViewAnimationUtils
at dejavu.appzonegroup.com.dejavuandroid.Fragment.PeopleTabFragment$1.onItemClick(PeopleTabFragment.java:59)
at android.widget.AdapterView.performItemClick(AdapterView.java:299)
at android.widget.AbsListView.performItemClick(AbsListView.java:1152)
ViewAnimationUtils was added in API level 21 (Lollipop 5.0). Are you testing your app on Lollipop? It will not work on older API levels. To use it you need to set min SDK to 21.
One google search brought me this, it gives you 3 ways to solve your problem. I'll quote a few wich I think that might resolve your problem:
1) Class is not available in Java Classpath.
2) You might be running your program using jar command and class was
not defined in manifest file's ClassPath attribute.
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 use the example from here with code here, about custom animations into a GridView.
All is working OK until I try to use a BitmapDrawable instead of a ColorDrawable.
mBackground = getResources().getDrawable(R.drawable.activity_background);
topView.setBackgroundDrawable(mBackground);
If using a BitmapDrawable, when swiping from main activity to the PictureDetailsActivity and back, makes the background from the main activity disappear. In logs I keep seeing this error when the background is gone (sometimes background is gone only when swiping the list, with the same error below):
Method getAlpha() with type int not found on target class
class android.graphics.drawable.BitmapDrawable
Searching the error I cannot find anything about it.
EDIT:
I am using alpha for fading in and out the background, as in the example:
// Fade in the background
ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0, 255);
bgAnim.setDuration(duration * 2);
bgAnim.start();
and
// Fade out background
ObjectAnimator bgAnim = ObjectAnimator.ofInt(mBackground, "alpha", 0);
bgAnim.setDuration(duration * 2);
bgAnim.start();
You are probably testing on a device/emulator which is < API19, while getAlpha() was only added on API19. Hence,
Method getAlpha() with type int not found
Please make sure to test on a target with minimum Android 4.4 (KitKat) installed.
I'm pretty much looking for exactly what the question suggests - a DecelerateAccelerateInterpolator. What I want to do is have an animation decelerate for the first half of the animation, and then accelerate after that. (I'm using it to mimic a gravity-like effect on a Bèzier curve).
EDIT:
Basically, what I'm looking for is something that as the object moves upward on the screen along a Bèzier curve, it decelerates until it gets to the top (at which point it momentarily stops, or has 0 speed or whatever), and then it starts to accelerate as it travels back down the other side.
If you're using Android API 21 or greater you can use PathInterpolator with a cubic Bezier curve:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
TimeInterpolator lInter = new PathInterpolator(0.0f, 0.97f, 1.0f, 0.03f);
animation.setInterpolator(lInter);
}
And here's a great website for calculating your desired bezier curve values:
Try to use my custom Interpolator. And you're welcome :)
mAnimation.setInterpolator(new Interpolator() {
#Override
public float getInterpolation(float pInput) {
System.out.println("input " + pInput);
// (1-(1-(x*2))^3)/2
return (float) (1 - Math.pow(1 - (2 * pInput), 3)) / 2;
}
});
I'm thinking you could actually chain two Interpolators. The first would be a DecelerateInterpolator from time 0 to time 1, then start an AccelerateInterpolator at time 1 to time 2. Basically split the time in half.