Animate an animation cancel - android

Say I'm animating a button from x=0 to x=200, using:
ObjectAnimator animator = ObjectAnimator.ofFloat(button, "x", 0f, 200f);
animator.setInterpolator(new AccelerateDecelerateInterpolator());
animator.start();
Now, when the button is at x=100, I want to cancel the animation. When I call animator.cancel(), the button stops abruptly. What I'm trying to achieve is that the x value gradually slows down (somewhat like a DecelerateInterpolator), and neatly comes to a stop at, say, x=120.
Ofcourse, I could just cancel() the animation and start a new decelerating animator. However, this does not take the current velocity of the button in account, leading to weird behavior.
How would I do this?

As correctly pointed out by #Gil, You have to deal with your custom Interpolator implementation. The good news is that you don't actually need to implement everything yourself. You can just combine 2 different interpolation formulas: accelerate/decelerate for the main animation and decelerate interpolator for cancellation.
Essentially that's what you are looking for:
Normal accelerate/decelerate animation:
Your animation when you cancel it somewhere in the middle:
Here is my quick interpolator implementation:
static class MyInterpolator extends AccelerateDecelerateInterpolator {
private float phaseShift = 0f;
private boolean isCancelled = false;
private float lastInput = 0f;
/**
* Maps a value representing the elapsed fraction of an animation to a value that represents
* the interpolated fraction. This interpolated value is then multiplied by the change in
* value of an animation to derive the animated value at the current elapsed animation time.
*
* #param input A value between 0 and 1.0 indicating our current point
* in the animation where 0 represents the start and 1.0 represents
* the end
* #return The interpolation value. This value can be more than 1.0 for
* interpolators which overshoot their targets, or less than 0 for
* interpolators that undershoot their targets.
*/
#Override
public float getInterpolation(float input) {
lastInput = input;
if(!isCancelled)
{
return super.getInterpolation(input);
}
else
{
return getCancellationInterpolation(input) - phaseShift;
}
}
public void cancel()
{
isCancelled = true;
this.phaseShift = getCancellationInterpolation(lastInput) - super.getInterpolation(lastInput);
}
private float getCancellationInterpolation(float input)
{
return (1.0f - (1.0f - input) * (1.0f - input));
}
}
As you can see, I use default interpolation for normal animation and switch do deceleration one when animation is canceled. Obviously this code is not perfect (it doesn't reset phaseShift value and isCancelled flag which causes miscalculation if you use repeat mode), but that's something you hopefully can figure out yourself :)
I created sample project on GitHub, so you can see how it looks like
FOLLOW UP
I played a bit more with formulas and taken the second part of DecelerateInterpolator implementation. I introduced factor parameter which helps you to control how fast your cancellation should happen (some sort of traction). Setting factor to 1.5 gives me this:
As you can see, when I hit cancel at ~0.5 point, animation gets cancelled more quickly (so it doesn't go all the way to the 0.7 of the distance as in previous example). It gives a bit better feeling of real object. Higher factor - faster your animation will stop.
Here is an updated interpolator:
static class MyInterpolator extends AccelerateDecelerateInterpolator {
......
private float factor = 1.5f;
.......
private float getCancellationInterpolation(float input)
{
if(factor == 1)
{
return (1.0f - (1.0f - input) * (1.0f - input));
}
else
{
return (float)(1.0f - Math.pow((1.0f - input), 2 * factor));
}
}
}

You run your animation all the way through and you implement your TimeInterpolator that "slows down" after accelerating.
You need to implement the method getInterpolation(float): this represents a mathematical function that maps time instants to your position values x normalised between 0 and 1. For instance, if you wanna run from x = 0 to x = 120, the value x = 100 corresponds to the normalised value
100/|120 - 0| = 0.83
Finding the right function requires some mathematical sophistication and some guessing, but this tutorial should get you started.

Related

animate view translationY on bottomsheet scroll

I would like to smoothly animate some views Y position based on the user scrolling a bottom sheet.
http://imgur.com/dVBlh83
However the animation is slightly jerky.
This is probably because scrolling a bottomsheet gives me a slideOffset at some interval, which is too infrequent to setTranlationY like I'm currently doing.
Is there a better way to do this.
Bottom sheet callback
BottomSheetBehavior.BottomSheetCallback() {
#Override public void onSlide(#NonNull View bottomSheetView, float slideOffset) {
int offset = (int) (slideOffset * 100);
if (offset > 0) {
scrollingHeaderView.animate(offset);
}
}
});
scrollingHeaderView
public void animate(int offset) {
position = -(offset / 2);
arrow.setTranslationY(offset * ANIMATION_RATIO_ARROW);
detailView.setTranslationY(offset * ANIMATION_RATIO);
}
I have tried using ObjectAnimator to create lots of small animations at each step.
ObjectAnimator.ofFloat(arrow, "translationY", arrow.getTranslationY(), position * ANIMATION_RATIO_ARROW).start();
in case it helps anyone else. I got it looking much better.
First i set the ration by pixel * range of values i'd get. So offset will be 0 -> 100. Mutliplied by the dp i want the view to move
private static int ANIMATION_RANGE = 100;
arrowScale = -(getResources().getDimensionPixelOffset(R.dimen.arrow_distance) / ANIMATION_RANGE);
containerScale = -(getResources().getDimensionPixelOffset(R.dimen.detail_distance) / ANIMATION_RANGE);
then as the value changes i set this using translationY
arrow.setTranslationY(position * arrowScale);
detailView.setTranslationY(position * containerScale);
I also needed a reset method, which animate back to the original position
public void reset() {
arrow.animate().setDuration(100).translationY(0);
detailView.animate().setDuration(100).translationY(0);
}

How to create custom methods to swipe my view up and down, Espresso - ViewActions

I want to create custom methods to swipe my view up and down, for example 25% down/up, or any other cases.
I tried to override methods like this:
public static ViewAction swipeDown(){
return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER,
GeneralLocation.TOP_CENTER, Press.FINGER);
}
But it's only from top to center and i need shorter ones. I wanted to use new CoordinatesProvider() :
public static ViewAction swipeDown(){
return new GeneralSwipeAction(Swipe.FAST, new CoordinatesProvider() {
#Override
public float[] calculateCoordinates(View view) {
return new float[0];
}
},
GeneralLocation.TOP_CENTER, Press.FINGER);
}
...and it might be an answer, but i don't really know how to calculate coordinates.
Robotium has already drag() function which is pretty similar to swipe[Direction] functions in Espresso, but it already uses defined coordinates.
* #param fromX X coordinate of the initial touch, in screen coordinates
* #param toX X coordinate of the drag destination, in screen coordinates
* #param fromY Y coordinate of the initial touch, in screen coordinates
* #param toY Y coordinate of the drag destination, in screen coordinates
* #param stepCount how many move steps to include in the drag. Less steps results in a faster drag
Check https://github.com/RobotiumTech/robotium/blob/b69dcf740baabec48c91999e523377faef79682e/robotium-solo/src/main/java/com/robotium/solo/Solo.java
You can use both frameworks along or try to rewrite Robotium's drag function in your own way.
This might be handful: https://github.com/piotrek1543/robotium-showcase

Change image height with delay animation

I'm using LibGDX engine in my game and I started to use Tween for animation:
I have in my game a small 'custom' progress bar. One image is an empty progress bar and the second is full one, they booths have the same width and height.What I do is I update it by setting the position of the full as the same as the empty, and when I want to display progress I use:
private Image fullProgress; //its a libGDX image type
//I have already set the process
float originalHeight = progressEmpty.getHeight();
howMuchProgress = originalHeight * process;
fullProgress.setHeight(howMuchProgress);
Any ideas how can I make it change the height with delay animation, so I can see it for a few seconds? better will be to use the Tween Engine?
Given your code example, delay should already be calculated in the process.
If you need to visualize the delay just for the debugging purposes (for example, for faking the AssetManager's loading progress), I suggest you do something like this:
private static final float LOADING_MIN_TIME= 2.0f; // delay amount in seconds
private float loadingTimer;
#Override
public void render(float delta) {
// ...
loadingTimer += delta;
float loadingTimerScaled = loadingTimer / LOADING_MIN_TIME;
if (loadingTimerScaled >= 1.0f) {
loadingTimerScaled = 1.0f;
}
process = Math.min(process, loadingTimerScaled); // use the slow(fake) value.
}

Gradually increase/decrease animation speed? [Android]

I was working on animated effect on android, I would like to know if there's any other way to gradually increase/decrease the animation speed?
Is it possible to specify like first 3 second rate of change was slow, and the rest goes fast?
Use a Interpolator. For your case I would recommend the AccelerateDecelerateInterpolator
Animation anim = AnimationUtils.loadAnimation(this, R.anim.your_animation);
anim.setInterpolator(new AccelerateDecelerateInterpolator());
image.startAnimation(anim);
As for the interpolator, you can build your own!
public class MyInterpolator extends Interpolator {
public MyInterpolator(int valueCount) {
super(valueCount);
}
public float getInterpolation (float input) {
return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}
}
Using Wolfram Alpha you can play with the parameters.

How to stop an animation (cancel() does not work)

I need to stop a running translate animation. The .cancel() method of Animation has no effect; the animation goes until the end anyway.
How do you cancel a running animation?
Call clearAnimation() on whichever View you called startAnimation().
On Android 4.4.4, it seems the only way I could stop an alpha fading animation on a View was calling View.animate().cancel() (i.e., calling .cancel() on the View's ViewPropertyAnimator).
Here's the code I'm using for compatibility before and after ICS:
public void stopAnimation(View v) {
v.clearAnimation();
if (canCancelAnimation()) {
v.animate().cancel();
}
}
... with the method:
/**
* Returns true if the API level supports canceling existing animations via the
* ViewPropertyAnimator, and false if it does not
* #return true if the API level supports canceling existing animations via the
* ViewPropertyAnimator, and false if it does not
*/
public static boolean canCancelAnimation() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
}
Here's the animation that I'm stopping:
v.setAlpha(0f);
v.setVisibility(View.VISIBLE);
// Animate the content view to 100% opacity, and clear any animation listener set on the view.
v.animate()
.alpha(1f)
.setDuration(animationDuration)
.setListener(null);
If you are using the animation listener, set v.setAnimationListener(null). Use the following code with all options.
v.getAnimation().cancel();
v.clearAnimation();
animation.setAnimationListener(null);
You must use .clearAnimation(); method in UI thread:
runOnUiThread(new Runnable() {
#Override
public void run() {
v.clearAnimation();
}
});
What you can try to do is get the transformation Matrix from the animation before you stop it and inspect the Matrix contents to get the position values you are looking for.
Here are the api's you should look into
public boolean getTransformation (long currentTime, Transformation outTransformation)
public Matrix getMatrix ()
public void getValues (float[] values)
So for example (some pseudo code. I have not tested this):
Transformation outTransformation = new Transformation();
myAnimation.getTransformation(currentTime, outTransformation);
Matrix transformationMatrix = outTransformation.getMatrix();
float[] matrixValues = new float[9];
transformationMatrix.getValues(matrixValues);
float transX = matrixValues[Matrix.MTRANS_X];
float transY = matrixValues[Matrix.MTRANS_Y];
Use the method setAnimation(null) to stop an animation, it exposed as public method in
View.java, it is the base class for all widgets, which are used to create interactive UI components (buttons, text fields, etc.).
/**
* Sets the next animation to play for this view.
* If you want the animation to play immediately, use
* {#link #startAnimation(android.view.animation.Animation)} instead.
* 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, and
* 2) the view's parent (which controls animations on its children)
* will be invalidated when the animation is supposed to
* start.
*
* #param animation The next animation, or null.
*/
public void setAnimation(Animation animation)
To stop animation you may set such objectAnimator that do nothing, e.g.
first when manual flipping there is animation left to right:
flipper.setInAnimation(leftIn);
flipper.setOutAnimation(rightOut);
then when switching to auto flipping there's no animation
flipper.setInAnimation(doNothing);
flipper.setOutAnimation(doNothing);
doNothing = ObjectAnimator.ofFloat(flipper, "x", 0f, 0f).setDuration(flipperSwipingDuration);
First of all, remove all the listeners which are related to the animatior or animator set. Then cancel the animator or animator set.
inwardAnimationSet.removeAllListeners()
inwardAnimationSet.cancel()
use this way:
// start animation
TranslateAnimation anim = new TranslateAnimation( 0, 100 , 0, 100);
anim.setDuration(1000);
anim.setFillAfter( true );
view.startAnimation(anim);
// end animation or cancel that
view.getAnimation().cancel();
view.clearAnimation();
cancel()
Cancel the animation. Canceling an animation invokes the animation
listener, if set, to notify the end of the animation.
If you cancel an animation manually, you must call reset()
before starting the animation again.
clearAnimation()
Cancels any animations for this view.
After going through all the things nothing worked. As I applied multiple animations in my views.
So below is the code that worked for me.
To start the animation that fades in and fade out continuously
val mAnimationSet = AnimatorSet()
private fun performFadeAnimation() {
val fadeOut: ObjectAnimator = ObjectAnimator.ofFloat(clScanPage, "alpha", 1f, 0f)
fadeOut.duration = 1000
val fadeIn: ObjectAnimator = ObjectAnimator.ofFloat(clScanPage, "alpha", 0f, 1f)
fadeIn.duration = 1000
mAnimationSet.play(fadeIn).after(fadeOut)
mAnimationSet.addListener(animationListener)
mAnimationSet.start()
}
The animation listener that loops in continuously
private val animationListener=object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
mAnimationSet.start()
}
}
To stop the animation that going on in a loop. I did the following.
private fun stopAnimation() {
mAnimationSet.removeAllListeners()
}
Since none of other answers mention it, you can easily stop an animation using ValueAnimator's cancel().
ValueAnimator is very powerful for making animations. Here is a sample code for creating a translation animation using ValueAnimator:
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0f, 5f);
int mDuration = 5000; //in millis
valueAnimator.setDuration(mDuration);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Update your view's x or y coordinate
}
});
valueAnimator.start();
You then stop the animation by calling
valueAnimator.cancel()`

Categories

Resources