Gradually increase/decrease animation speed? [Android] - 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.

Related

Animate an animation cancel

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.

Android ValueAnimator pauses during repeat

So I'm using a ValueAnimator to animate a stick figure's limbs from one position to another, in an infinite loop, or at least until the animation is stopped. My problem is that when the animator repeats I have a slight pause as if the animation is lagging behind, but it only happens when the animation repeats. I have other animations that only happen once and those run perfectly smoothly, and they have just as much computation each time so I'm currently thinking that it's a problem with the ValueAnimator.
In the past I was able to find other people complaining about this problem, but I haven't been able to find anyone who has found a solution. Do you guys know if this is a real problem with the Android ValueAnimator? If so, do you know of any solutions? If not, do you guys have any ideas as to why this could be happening to me in just that one place in the animation? I'm really stuck on this one.
My code for the ValueAnimator setup is this:
mFigureAnimator = ValueAnimator.ofFloat(0f, 1f);
mFigureAnimator.setInterpolator(new LinearInterpolator());
mFigureAnimator.setDuration(1000);
mFigureAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
Float delta = (Float)animation.getAnimatedValue();
// Set the drawn locations based on the animated time and the start/end
invalidate();
}
});
mFigureAnimator.setRepeatCount(ValueAnimator.INFINITE);
mFigureAnimator.setRepeatMode(ValueAnimator.RESTART);
mFigureAnimator.start();
for Animation, you can configure the interpolator as LinearInterpolator in the animation file :
android:interpolator="#android:anim/linear_interpolator"
for Animator, LinearInterpolator also work for me, I had a rotate animator, do 360 degrees rotation and repeat infinite:
public class RotateAnimator {
private float mDegrees;
private ObjectAnimator mAnim;
private RotateAnimator() {
mAnim = ObjectAnimator.ofFloat(this, "degrees", 360);
mAnim.setInterpolator(new LinearInterpolator());
mAnim.setRepeatCount(ValueAnimator.INFINITE);
mAnim.setRepeatMode(ValueAnimator.INFINITE);
mAnim.setEvaluator(new FloatEvaluator());
mAnim.setDuration(2000);
mAnim.start();
}
public float getDegrees() {
return mDegrees;
}
public void setDegrees(float degrees) {
this.mDegrees = degrees;
// invalidate the view so it can redraw itself
invalidate();
}
}
that way solved my problem, if you couldn't find another solution, hope this can help you, good luck.

How to move a image across the screen on Android

How to move an image across the screen android?
Guys, im creating MoleMash game for My Coursework and I really need help!. I want to try to move a image (the mole) across the screen in different and random coordinates so that it can be touched by the user to get a score. I created the background which I made the image view. I have the mole, Is it another image view? or is it a Image button?
And how do I move this across to different parts of the screen for the user interact?
I would greatly appreciate your help!
For games development you need to learn how to use canvas and SurfaceView: How can I use the animation framework inside the canvas?
Using onTouch event listener you would compare the touch location with the location of your animated images...
The mole would be an image button so it could be clicked, but to make it move you would have to use animation. Keep in mind that I am a beginner in Android animation as well :). You would probably use TranslateAnimation and set the x and y in the parameters to random variables, the duration could be when a time/counter variable reaches a certain point after the game has started. This post has more information on animation: How to move an image from left to right in android.
Try this code,
declare following variables
private Random random=new Random();
private int screenwidth, screenhgt;
In onCreate():
screenwidth= getyourScreenWidth;
screenhgt=getyourscreenheight;
Pass your view to this method(In my case I animate textview.. you pass the imageview)
private void screenRandomAnimator(final TextView textView) {
final AnimatorSet mAnimatorSet = new AnimatorSet();
mAnimatorSet.playTogether(ObjectAnimator.ofFloat(textView, "x", (float) random.nextInt(screenwidth), (float) random.nextInt(screenwidth)),
ObjectAnimator.ofFloat(textView, "y", (float) random.nextInt(screenhgt), (float) random.nextInt(screenhgt)), ObjectAnimator.ofFloat(textView, "rotation", 360)
/* ObjectAnimator.ofFloat(textView, "scaleX", 1, 0.8f, 1, 1.1f, 1), ObjectAnimator.ofFloat(textView, "scaleY", 1, 0.8f, 1, 1.1f, 1)*/);
int Low = 1500;
int High = 2500;
int Result = random.nextInt(High - Low) + Low;
mAnimatorSet.setDuration(Result);
mAnimatorSet.start();
mAnimatorSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mAnimatorSet.playTogether(ObjectAnimator.ofFloat(textView, "x", textView.getX(), (float) random.nextInt(screenwidth)),
ObjectAnimator.ofFloat(textView, "y", textView.getY(), (float) random.nextInt(screenhgt)));
int Low = 1500;
int High = 2500;
int Result = random.nextInt(High - Low) + Low;
mAnimatorSet.setDuration(Result);
mAnimatorSet.start();
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}

Creating a 3D flip animation in Android using XML

I have created a 3D flip of a view using this android tutorial
However, I have done it programmatically and I would like to do it all in xml, if possible. I am not talking about simply shrinking a view to the middle and then back out, but an actual 3D flip.
Is this possible via xml?
Here is the answer, though it only works with 3.0 and above.
1) Create a new resources folder called "animator".
2) Create a new .xml file which I will call "flipping". Use the following xml code:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:valueFrom="0" android:valueTo="360" android:propertyName="rotationY" >
</objectAnimator>
No, the objectAnimator tags do not start with an uppercase "O".
3) Start the animation with the following code:
ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(mContext, R.animator.flipping);
anim.setTarget(A View Object reference goes here i.e. ImageView);
anim.setDuration(3000);
anim.start();
I got all this from here.
Since the answers to this question are fairly dated, here is a more modern solution relying on ValueAnimators.
This solution implements a true, visually appealing 3D-flip, because it not just flips the view, but also scales it while it is flipping (this is how Apple does it).
First we set up the ValueAnimator:
mFlipAnimator = ValueAnimator.ofFloat(0f, 1f);
mFlipAnimator.addUpdateListener(new FlipListener(frontView, backView));
And the corresponding update listener:
public class FlipListener implements ValueAnimator.AnimatorUpdateListener {
private final View mFrontView;
private final View mBackView;
private boolean mFlipped;
public FlipListener(final View front, final View back) {
this.mFrontView = front;
this.mBackView = back;
this.mBackView.setVisibility(View.GONE);
}
#Override
public void onAnimationUpdate(final ValueAnimator animation) {
final float value = animation.getAnimatedFraction();
final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f));
if(value <= 0.5f){
this.mFrontView.setRotationY(180 * value);
this.mFrontView.setScaleX(scaleValue);
this.mFrontView.setScaleY(scaleValue);
if(mFlipped){
setStateFlipped(false);
}
} else {
this.mBackView.setRotationY(-180 * (1f- value));
this.mBackView.setScaleX(scaleValue);
this.mBackView.setScaleY(scaleValue);
if(!mFlipped){
setStateFlipped(true);
}
}
}
private void setStateFlipped(boolean flipped) {
mFlipped = flipped;
this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE);
this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE);
}
}
That's it!
After this setup you can flip the views by calling
mFlipAnimator.start();
and reverse the flip by calling
mFlipAnimator.reverse();
If you want to check if the view is flipped, implement and call this function:
private boolean isFlipped() {
return mFlipAnimator.getAnimatedFraction() == 1;
}
You can also check if the view is currently flipping by implementing this method:
private boolean isFlipping() {
final float currentValue = mFlipAnimator.getAnimatedFraction();
return (currentValue < 1 && currentValue > 0);
}
You can combine the above functions to implement a nice function to toggle the flip, depending on if it is flipped or not:
private void toggleFlip() {
if(isFlipped()){
mFlipAnimator.reverse();
} else {
mFlipAnimator.start();
}
}
That's it! Simple and easy. Enjoy!
I have created a simple program for creating flip of view like :
In Activity you have to create this method, for adding flip_rotation in view.
private void applyRotation(View view)
{
final Flip3dAnimation rotation = new Flip3dAnimation(view);
rotation.applyPropertiesInRotation();
view.startAnimation(rotation);
}
for this, you have to copy main class used to provide flip_rotation.
import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;
public class Flip3dAnimation extends Animation {
private final float mFromDegrees;
private final float mToDegrees;
private final float mCenterX;
private final float mCenterY;
private Camera mCamera;
public Flip3dAnimation(View view) {
mFromDegrees = 0;
mToDegrees = 720;
mCenterX = view.getWidth() / 2.0f;
mCenterY = view.getHeight() / 2.0f;
}
#Override
public void initialize(int width, int height, int parentWidth,
int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
mCamera = new Camera();
}
public void applyPropertiesInRotation()
{
this.setDuration(2000);
this.setFillAfter(true);
this.setInterpolator(new AccelerateInterpolator());
}
#Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final float fromDegrees = mFromDegrees;
float degrees = fromDegrees
+ ((mToDegrees - fromDegrees) * interpolatedTime);
final float centerX = mCenterX;
final float centerY = mCenterY;
final Camera camera = mCamera;
final Matrix matrix = t.getMatrix();
camera.save();
Log.e("Degree",""+degrees) ;
Log.e("centerX",""+centerX) ;
Log.e("centerY",""+centerY) ;
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);
}
}
The tutorial or the link by om252345 don't produce believable 3D flips. A simple rotation on the y-axis isn't what's done in iOS. The zoom effect is also needed to create that nice flip feel. For that, take a look at this example.
There is also a video here.
One of the better solution to flip the image with out use of the resource animation , is as follow:-
ObjectAnimator animation = ObjectAnimator.ofFloat(YOUR_IMAGEVIEW, "rotationY", 0.0f, 360f); // HERE 360 IS THE ANGLE OF ROTATE, YOU CAN USE 90, 180 IN PLACE OF IT, ACCORDING TO YOURS REQUIREMENT
animation.setDuration(500); // HERE 500 IS THE DURATION OF THE ANIMATION, YOU CAN INCREASE OR DECREASE ACCORDING TO YOURS REQUIREMENT
animation.setInterpolator(new AccelerateDecelerateInterpolator());
animation.start();
The simplest way to do it is using ViewPropertyAnimator
mImageView.animate().rotationY(360f);
Using the fluent interface you can build more complex and exciting animation.
E.g. you can enable hardware acceleration just call withLayer() method(API 16). More here
If you want to figure out how to create 3d flick animation, please follow here and here
I implemended my own solution only for a research. It includes: cancelation, accelleration, support API >= 15 and is based on Property Animation.
The entire animation includes 4 parts, 2 for each side.
Every objectAnimator has a listener that defines current animation index and represents an image in the onAnimationStart and current play time value in the onAnimationCancel.
It looks like
mQuarterAnim1.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
mQuarterCurrentAnimStartIndex = QUARTER_ANIM_INDEX_1;
mImageView.setImageResource(mResIdFrontCard);
}
#Override
public void onAnimationCancel(Animator animation) {
mQuarterCurrentAnimPlayTime = ((ObjectAnimator) animation).getCurrentPlayTime();
}
});
For start set call
mAnimatorSet.play(mQuarterAnim1).before(mQuarterAnim2)
If AnimatorSet was canceled we can calculate delta and run the reverse animation relying on the current index animation and the current play time value.
long degreeDelta = mQuarterCurrentAnimPlayTime * QUARTER_ROTATE / QUARTER_ANIM_DURATION;
if (mQuarterCurrentAnimStartIndex == QUARTER_ANIM_INDEX_1) {
mQuarterAnim4.setFloatValues(degreeDelta, QUARTER_FROM_1);
mQuarterAnim4.setDuration(mQuarterCurrentAnimPlayTime);
mAnimatorSet.play(mQuarterAnim4);
}
A full code snippet you can find here
Just put the view which you're going to animate it in place of viewToFlip.
ObjectAnimator flip = ObjectAnimator.ofFloat(viewToFlip, "rotationY", 0f, 360f); // or rotationX
flip.setDuration(2000); // 2 seconds
flip.start();
Adding to A. Steenbergen's great answer. When flipping the same view (updating a TextView for example) I removed the View.Visibility change in the constructor in order to keep the transition smoother.
public FlipListener(final View front, final View back) {
this.mFrontView = front;
this.mBackView = back;
}

Reversing an Animation

I have an ImageView that gets animated when it is added to a layout.
When it is removed, I want to reverse the same animation.
Is there a way to reverse an animation in android without recoding it and reversing the parameters?
No, sadly you cannot do it with the Animation object.
But you can simulate it using an interpolator that will inverse the animation:
package com.example.android;
import android.view.animation.Interpolator;
public class ReverseInterpolator implements Interpolator {
#Override
public float getInterpolation(float paramFloat) {
return Math.abs(paramFloat -1f);
}
}
Then on your animation you can set your new interpolator:
myAnimation.setInterpolator(new ReverseInterpolator());
If you are using Object or ValueAnimator to animate the view, you can simply do
ValueAnimator myAnimator = new ValueAnimator();
myAnimator.reverse()
Documentation can be found here.
Based on pcans idea, you can reverse any interpolator, not just linear.
class ReverseInterpolator implements Interpolator{
private final Interpolator delegate;
public ReverseInterpolator(Interpolator delegate){
this.delegate = delegate;
}
public ReverseInterpolator(){
this(new LinearInterpolator());
}
#Override
public float getInterpolation(float input) {
return 1 - delegate.getInterpolation(input);
}
}
Usage
ReverseInterpolator reverseInterpolator = new ReverseInterpolator(new AccelerateInterpolator())
myAnimation.setInterpolator(reverseInterpolator);
I have a similar approach to pcans buts slightly different. It takes an Interpolator and will effectively pass out values that would be the same as using the passed in Interpolator normally and then in REVERSE mode. Saves you having to think about the buggy implementations of Animation.REVERSE across Android. See the code here
public class ReverseInterpolator implements Interpolator {
private final Interpolator mInterpolator;
public ReverseInterpolator(Interpolator interpolator){
mInterpolator = interpolator;
}
#Override
public float getInterpolation(float input) {
return mInterpolator.getInterpolation(reverseInput(input));
}
/**
* Map value so 0-0.5 = 0-1 and 0.5-1 = 1-0
*/
private float reverseInput(float input){
if(input <= 0.5)
return input*2;
else
return Math.abs(input-1)*2;
}
}
Simplest solution i came up with
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:interpolator="#android:anim/linear_interpolator">
<alpha
android:duration="2000"
android:fromAlpha="0.1"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toAlpha="1.0">
</alpha>
</set>
You can make the code remember the original position and the end position. And let your code dynamically get those values when triggering animation.
If you are using animation from xml then an easy way is to made an exact same reverse animation to original animation. Add Animation.AnimationListener to original animation and in onAnimationEnd method start the reverse animation.
this worked for me
ObjectAnimator anim = ObjectAnimator.ofFloat(imageViewUpb, "rotation", rotationAngle, rotationAngle + 180);
if (linearLayoutb.getVisibility()==GONE){
linearLayoutb.setVisibility(VISIBLE);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%360;
imageViewUpb.animate().rotation(rotationAngle).setDuration(500).start();
}else{
linearLayoutb.setVisibility(GONE);
anim.setDuration(500);
anim.start();
rotationAngle += 180;
rotationAngle = rotationAngle%180;
imageViewUpDownb.animate().rotation(rotationAngle).setDuration(500).start();
}
linearlayoutb is the view that expands when the imageviewUpb faces up
make int rotationAngle = 0; global parameter
You need to use RepeatCount and RepeatMode
kotlin
var anim = TranslateAnimation(0, 100, 0, 100)
anim.repeatCount = Animation.INFINITE // you cant 1,2,...
anim.repeatMode = Animation.REVERSE // you can set REVERSE or RESTART
anim.start()
Since other answers have already been answered in Java, I will answer in Kotlin
// code inside onViewCreated()
...
var anim: TranslateAnimation = TranslateAnimation(0F,60F,0F, 0F)
anim.duration = 3000
anim.fillAfter = true
anim.repeatCount = -1
anim.repeatMode = Animation.REVERSE // back and forth
binding.asteroid.startAnimation(anim)
...
This is the simplest approach in Kotlin.

Categories

Resources