How to make a smooth image rotation in Android? - android

I'm using a RotateAnimation to rotate an image that I'm using as a custom cyclical spinner in Android. Here's my rotate_indefinitely.xml file, which I placed in res/anim/:
<?xml version="1.0" encoding="UTF-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:duration="1200" />
When I apply this to my ImageView using AndroidUtils.loadAnimation(), it works great!
spinner.startAnimation(
AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely) );
The one problem is that the image rotation seems to pause at the top of every cycle.
In other words, the image rotates 360 degrees, pauses briefly, then rotates 360 degrees again, etc.
I suspect that the problem is that the animation is using a default interpolator like android:iterpolator="#android:anim/accelerate_interpolator" (AccelerateInterpolator), but I don't know how to tell it not to interpolate the animation.
How can I turn off interpolation (if that is indeed the problem) to make my animation cycle smoothly?

You are right about AccelerateInterpolator; you should use LinearInterpolator instead.
You can use the built-in android.R.anim.linear_interpolator from your animation XML file with android:interpolator="#android:anim/linear_interpolator".
Or you can create your own XML interpolation file in your project, e.g. name it res/anim/linear_interpolator.xml:
<?xml version="1.0" encoding="utf-8"?>
<linearInterpolator xmlns:android="http://schemas.android.com/apk/res/android" />
And add to your animation XML:
android:interpolator="#anim/linear_interpolator"
Special Note: If your rotate animation is inside a set, setting the interpolator does not seem to work. Making the rotate the top element fixes it. (this will save your time.)

I had this problem as well, and tried to set the linear interpolator in xml without success. The solution that worked for me was to create the animation as a RotateAnimation in code.
RotateAnimation rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(5000);
rotate.setInterpolator(new LinearInterpolator());
ImageView image= (ImageView) findViewById(R.id.imageView);
image.startAnimation(rotate);

This works fine
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1600"
android:fromDegrees="0"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="358" />
To reverse rotate:
<?xml version="1.0" encoding="UTF-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1600"
android:fromDegrees="358"
android:interpolator="#android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:toDegrees="0" />

Maybe, something like this will help:
Runnable runnable = new Runnable() {
#Override
public void run() {
imageView.animate().rotationBy(360).withEndAction(this).setDuration(3000).setInterpolator(new LinearInterpolator()).start();
}
};
imageView.animate().rotationBy(360).withEndAction(runnable).setDuration(3000).setInterpolator(new LinearInterpolator()).start();
By the way, you can rotate by more than 360 like:
imageView.animate().rotationBy(10000)...

Try using toDegrees="359" since 360° and 0° are the same.

ObjectAnimator.ofFloat(view, View.ROTATION, 0f, 360f).setDuration(300).start();
Try this.

Rotation Object programmatically.
// clockwise rotation :
public void rotate_Clockwise(View view) {
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 180f, 0f);
// rotate.setRepeatCount(10);
rotate.setDuration(500);
rotate.start();
}
// AntiClockwise rotation :
public void rotate_AntiClockwise(View view) {
ObjectAnimator rotate = ObjectAnimator.ofFloat(view, "rotation", 0f, 180f);
// rotate.setRepeatCount(10);
rotate.setDuration(500);
rotate.start();
}
view is object of your ImageView or other widgets.
rotate.setRepeatCount(10); use to repeat your rotation.
500 is your animation time duration.

Pruning the <set>-Element that wrapped the <rotate>-Element solves the problem!
Thanks to Shalafi!
So your Rotation_ccw.xml should loook like this:
<?xml version="1.0" encoding="utf-8"?>
<rotate
xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="-360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="2000"
android:fillAfter="false"
android:startOffset="0"
android:repeatCount="infinite"
android:interpolator="#android:anim/linear_interpolator"
/>

In Kotlin:
ivBall.setOnClickListener(View.OnClickListener {
//Animate using XML
// val rotateAnimation = AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely)
//OR using Code
val rotateAnimation = RotateAnimation(
0f, 359f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
)
rotateAnimation.duration = 300
rotateAnimation.repeatCount = 2
//Either way you can add Listener like this
rotateAnimation.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation?) {
}
override fun onAnimationRepeat(animation: Animation?) {
}
override fun onAnimationEnd(animation: Animation?) {
val rand = Random()
val ballHit = rand.nextInt(50) + 1
Toast.makeText(context, "ballHit : " + ballHit, Toast.LENGTH_SHORT).show()
}
})
ivBall.startAnimation(rotateAnimation)
})

As hanry has mentioned above putting liner iterpolator is fine. But if rotation is inside a set you must put android:shareInterpolator="false" to make it smooth.
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
**android:shareInterpolator="false"**
>
<rotate
android:interpolator="#android:anim/linear_interpolator"
android:duration="300"
android:fillAfter="true"
android:repeatCount="10"
android:repeatMode="restart"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator"
android:duration="3000"
android:fillAfter="true"
android:pivotX="50%"
android:pivotY="50%"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="0"
android:toYScale="0" />
</set>
If Sharedinterpolator being not false, the above code gives glitches.

If you are using a set Animation like me you should add the interpolation inside the set tag:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/linear_interpolator">
<rotate
android:duration="5000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:startOffset="0"
android:toDegrees="360" />
<alpha
android:duration="200"
android:fromAlpha="0.7"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:toAlpha="1.0" />
</set>
That Worked for me.

No matter what I tried, I couldn't get this to work right using code (and setRotation) for smooth rotation animation. What I ended up doing was making the degree changes so small, that the small pauses are unnoticeable. If you don't need to do too many rotations, the time to execute this loop is negligible. The effect is a smooth rotation:
float lastDegree = 0.0f;
float increment = 4.0f;
long moveDuration = 10;
for(int a = 0; a < 150; a++)
{
rAnim = new RotateAnimation(lastDegree, (increment * (float)a), Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rAnim.setDuration(moveDuration);
rAnim.setStartOffset(moveDuration * a);
lastDegree = (increment * (float)a);
((AnimationSet) animation).addAnimation(rAnim);
}

Try to use more than 360 to avoid restarting.
I use 3600 insted of 360 and this works fine for me:
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="3600"
android:interpolator="#android:anim/linear_interpolator"
android:repeatCount="infinite"
android:duration="8000"
android:pivotX="50%"
android:pivotY="50%" />

Here is code snippet that worked fine for me:
RotateAnimation rotate = new RotateAnimation(
0, 359,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotate.setDuration(1500);
rotate.setRepeatCount(Animation.INFINITE);
yourView.startAnimation(rotate);
Check it's not 360 it's 359 in the constructor as 0 and 360 are at the same point.

Is it possible that because you go from 0 to 360, you spend a little bit more time at 0/360 than you are expecting? Perhaps set toDegrees to 359 or 358.

private fun rotateTheView(view: View?, startAngle: Float, endAngle: Float) {
val rotate = ObjectAnimator.ofFloat(view, "rotation", startAngle, endAngle)
//rotate.setRepeatCount(10);
rotate.duration = 400
rotate.start()
}

The 100% correct answer
var rotate = RotateAnimation(
0F, 360F,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
)
rotate.duration = 999
rotate.repeatCount = Animation.INFINITE
imageview..setAnimation(rotate)
100% working!!!

In Android, if you want to animate an object and make it move an object from location1 to location2, the animation API figures out the intermediate locations (tweening) and then queues onto the main thread the appropriate move operations at the appropriate times using a timer. This works fine except that the main thread is usually used for many other things — painting, opening files, responding to user inputs etc. A queued timer can often be delayed. Well written programs will always try to do as many operations as possible in background (non main) threads however you can’t always avoid using the main thread. Operations that require you to operate on a UI object always have to be done on the main thread. Also, many APIs will funnel operations back to the main thread as a form of thread-safety.
Views are all drawn on the same GUI thread which is also used for all user interaction.
So if you need to update GUI rapidly or if the rendering takes too much time and affects user experience then use SurfaceView.
Example of rotation image:
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {
private DrawThread drawThread;
public MySurfaceView(Context context) {
super(context);
getHolder().addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
drawThread = new DrawThread(getHolder(), getResources());
drawThread.setRunning(true);
drawThread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
drawThread.setRunning(false);
while (retry) {
try {
drawThread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
}
class DrawThread extends Thread{
private boolean runFlag = false;
private SurfaceHolder surfaceHolder;
private Bitmap picture;
private Matrix matrix;
private long prevTime;
public DrawThread(SurfaceHolder surfaceHolder, Resources resources){
this.surfaceHolder = surfaceHolder;
picture = BitmapFactory.decodeResource(resources, R.drawable.icon);
matrix = new Matrix();
matrix.postScale(3.0f, 3.0f);
matrix.postTranslate(100.0f, 100.0f);
prevTime = System.currentTimeMillis();
}
public void setRunning(boolean run) {
runFlag = run;
}
#Override
public void run() {
Canvas canvas;
while (runFlag) {
long now = System.currentTimeMillis();
long elapsedTime = now - prevTime;
if (elapsedTime > 30){
prevTime = now;
matrix.preRotate(2.0f, picture.getWidth() / 2, picture.getHeight() / 2);
}
canvas = null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(picture, matrix, null);
}
}
finally {
if (canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
activity:
public class SurfaceViewActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MySurfaceView(this));
}
}

you can use this code:
view.animate().rotation(360.0f).setDuration(1000);

Related

How do i apply fade out animation from left to right in android

I am trying to make a fade out animation to a View, but I don't want the standard one. I want the fade out to start from left to right (or right to left no matter). Any idea on how to do it? I didn't really found anything for android on SO. This is my code for the moment:
val fadeOut = AlphaAnimation(1f, 0f)
fadeOut.interpolator = AccelerateInterpolator()
fadeOut.duration = 3000L
fadeOut.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) {
}
override fun onAnimationEnd(animation: Animation) {
mTextView.visibility = View.VISIBLE
}
override fun onAnimationRepeat(animation: Animation) {}
})
mTextView.startAnimation(fadeOut)
From what I can understand, it sounds like you want a reveal animation rather than a fade in/out animation. Your best bet would be to write your own reveal Animation, but if you're ready to settle for a simpler hack, then you could use the circular reveal animation that's available in ViewAnimationUtils. Just start the circular far off to the left/right so that the circular reveal feels like a linear reveal.
final View myView = findViewById(R.id.animatedImageView);
int cx = myView.getMeasuredWidth() * 2;
int cy = myView.getMeasuredHeight() / 2;
int finalRadius = (int) Math.hypot(myView.getWidth()*2, myView.getHeight());
Animator anim;
// for reveal
anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
// for hiding
// anim = ViewAnimationUtils.createCircularReveal(myView, cx, cy, finalRadius, 0);
// Do note that you'll need to factor in the 'reveal' time taken in by the extra empty space.
// You could probably get around this by creating your own interpolator,
// but in which case, you might as well just create your own reveal animation.
anim.setDuration(3000);
// make the view visible and start the animation
myView.setVisibility(View.VISIBLE);
anim.start();
Try this:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<alpha
android:duration="1000"
android:fromAlpha="0.0"
android:interpolator="#android:anim/accelerate_interpolator"
android:toAlpha="1.0" />
<translate
android:fromXDelta="100%p" android:toXDelta="0"
android:duration="500" />
</set>

Click on view during animation doesn't react

I have a simple animation:
<rotate android:fromDegrees="0"
android:toDegrees="180"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="4"
android:repeatMode="reverse"
android:duration="1000"
android:interpolator="#android:anim/linear_interpolator" />
<translate
android:duration="2000"
android:fromXDelta="10%p"
android:toXDelta="90%p"
android:repeatCount="1"
android:repeatMode="reverse"
android:fillAfter="true"/>
and I run it on an ImageView. I have an onclick event setup on the ImageView:
imgCorrect1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "explode ");
}
});
Here is how I start the animation:
Animation anim = AnimationUtils.loadAnimation(this, R.anim.rottrans);
imgCorrect1.startAnimation(Anim1);
The problem is that while the animation is going, the onclick doesn't get called while the animation is running if I tap on the image while it's animating.
I have tried to search on this issue, but the posts are all to animate an ImageView -when- the onclick is called, not the opposite.
I hope I have been clear here in the description. I have tried to provide all of the relevant code to explain the issue.
You are using Animation API, which animates the matrix of the view. Thus you see a ghost of the original view being animated, whereas the view still is positioned in the original place.
Instead, use Animator API. Here's how it may look like:
ImageView imageView = ...;
ObjectAnimator rotate = ObjectAnimator.ofFloat(imageView, View.ROTATION, 0, 180);
rotate.setRepeatCount(4);
rotate.setRepeatMode(ValueAnimator.REVERSE);
rotate.setDuration(1000);
rotate.setInterpolator(new LinearInterpolator());
ObjectAnimator translate = ObjectAnimator.ofFloat(imageView, View.TRANSLATION_X, 0, 100);
translate.setRepeatCount(1);
translate.setRepeatMode(ValueAnimator.REVERSE);
translate.setDuration(2000);
AnimatorSet set = new AnimatorSet();
// Play either sequentially or together
set.playSequentially(rotate, translate);
// set.playTogether(rotate, translate);
set.start();

How can i create left to right and then inverse animation in android?

How can I create one animation which always translates from left to right and then if animation stops it turns the other way and translates from right to left. I can create Translate animation in xml and programatically too.
Create this xml, it will translate from left to right and from right to left.
/res/anim/translate.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillEnabled="true"
android:fillAfter="true" >
<translate
android:interpolator="#android:anim/linear_interpolator"
android:fromXDelta="0%p"
android:toXDelta="10%p"
android:duration="2000"
android:startOffset="0" />
<translate
android:interpolator="#android:anim/linear_interpolator"
android:fromXDelta="10%p"
android:toXDelta="-10%p"
android:duration="2000"
android:startOffset="2000" />
</set>
Suppose you want to apply this animation to an image then write the code below in your class file.
ImageView image = (ImageView)findViewById(R.id.imageView1);
Animation animation = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.translate);
image.startAnimation(animation);
Edit:
If you want your animation to repeat infinitely add the following attributes to the translate tag.
android:repeatCount="infinite"
android:repeatMode="restart"
Depends on your min API level actually. Essentially You should look at ViewPropertyAnimator class.
Let's say you have view to animate, and parent - parent of this view. Your code would look like this:
final float startX = 0; //start position
final float endX = parent.getWidth() - view.getWidth(); //end position - right edge of the parent
API 12+:
view.animate().translationX(endX).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
view.animate().translationX(startX).start();
}
}).start();
API 16+:
view.animate().translationX(endX).withEndAction(new Runnable() {
#Override
public void run() {
view.animate().translationX(startX).start();
}
}).start();
Your startX and endX might be different - depending on your needs.
Please note that start() method call is optional.
Also I have not mentioned solution for API <12 since I personally think that nobody should support those legacy APIs :)

Start two animation in the same Layout

Im trying to do an alpha and translate in a RelativeLayout. I define both:
AlphaAnimation alpha;
alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1500);
alpha.setFillAfter(true);
TranslateAnimation translate;
translate = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 0,
Animation.RELATIVE_TO_SELF, 1,
Animation.RELATIVE_TO_SELF, 0);
translate.setDuration(1000);
So I start the animation in my RelativeLayout
RelativeLayout.startAnimation(translate);
RelativeLayout.startAnimation(alpha);
The problem is that in this case, only the alpha animation start and not the translation. Can someone help me? The question is how can I start two different animations at the same time in the same object(Relative Layout in my case)
I resolve the question. I added it:
AnimationSet animationSet = new AnimationSet(true);
animationSet.addAnimation(alpha);
animationSet.addAnimation(translate);
RelativeLayout.startAnimation(animationSet);
You can use animation set ıf you want to run two animation in the same time:
http://developer.android.com/reference/android/view/animation/AnimationSet.html
For exeample;
as = new AnimationSet(true);
as.setFillEnabled(true);
as.setInterpolator(new BounceInterpolator());
TranslateAnimation ta = new TranslateAnimation(-300, 100, 0, 0);
ta.setDuration(2000);
as.addAnimation(ta);
TranslateAnimation ta2 = new TranslateAnimation(100, 0, 0, 0);
ta2.setDuration(2000);
ta2.setStartOffset(2000); // allowing 2000 milliseconds for ta to finish
as.addAnimation(ta2);
Your current code won't work, because as soon as the first animation starts, the second one ends it and starts itself. So you need to wait till it's done.
Try this :
translate.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
RelativeLayout.startAnimation(alpha);
}
});
If you want to execute them simultaneously, I'd suggest you create an animation.xml file in your res/anim/ folder.
Example:
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale=".75"
android:toYScale=".75"
android:duration="1500"/>
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:duration="1500"
android:pivotX="50%"
android:pivotY="50%" />
<scale
android:fromXScale=".75"
android:fromYScale=".75"
android:toXScale="1"
android:toYScale="1"
android:duration="1500"/>
</set>
Java Code:
Animation multiAnim = AnimationUtils.loadAnimation(this, R.anim.animation);
RelativeLayout.startAnimation(multiAnim);

Android animation does not repeat

I'm trying to make simple animation that would repeat several times (or infinitely).
It seems that android:repeatCount does not work!
Here is my animation resource from /res/anim/first_animation.xml :
<?xml version="1.0" encoding="utf-8"?>
<set
xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
android:repeatCount="infinite"
>
<scale
android:interpolator="#android:anim/decelerate_interpolator"
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.2"
android:toYScale="1.2"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false" />
<scale
android:interpolator="#android:anim/accelerate_interpolator"
android:startOffset="500"
android:duration="500"
android:fromXScale="1.2"
android:fromYScale="1.2"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="false" />
</set>
First it should scale image from 1.0 to 1.2 size in 500 ms.
And then scale it back to 1.0 in 500 ms.
Here is how I'm using it:
Animation firstAnimation = AnimationUtils.loadAnimation(this, R.anim.first_animation);
imgView.startAnimation(firstAnimation);
It makes one cycle and then finishes.
It scales up, then scales down ans then stops.
How can I make this work as intended?
Update: Back in Sep, 2011 an Android engineer fixed this issue for the most part. The attributes that were ignored in XML now work, with the exception of repeatCount and fillEnabled which are still ignored (on purpose for some reason). This means it still isn't easy to repeat an AnimationSet unfortunately.
For details please see the overview in the updated docs (explains which attributes are ignored, which work, and which are passed onto children). And for a deeper understanding of what fillAfter, fillBefore, and fillEnabled actually do, see the engineer's (Chet Haase) blog post about it here.
Original Answer
To expand upon answers by Pavel and others: it is true that the <set> tag is ridiculously buggy. It can't deal correctly with repeatCount and a number of other attributes.
I spent a few hours figuring out what it can and can't deal with and have submitted a bug report/issue here: Issue 17662
In summary (this concerns AnimationSets):
setRepeatCount() / android:repeatCount
This attribute (as well as repeatMode) does not work in code or XML. This makes repeating an entire set of animations difficult.
setDuration() / android:duration
Setting this on an AnimationSet in code WORKS (overrides all durations of children animations), but not when included in the tag in XML
setFillAfter() / android:fillAfter
This works in both code and XML for the tag. Strangely I have gotten it to also work without the need to set fillEnabled to true.
setFillBefore() / android:fillBefore
Seems to have no effect/ignored in both code and XML
setFillEnabled() / android:fillEnabled
Seems to have no effect/ignored in both code and XML. I can still get fillAfter to work even without including fillEnabled or setting fillEnabled to false.
setStartOffset() / android:startOffset
This works only in code and not XML.
I've found that <set> tag has buggy implementation in class AnimationSet.
It can't deal correctly with repeatCount.
What we can do - is to set repeatCount directly in <scale> tag.
This XML resource is working well:
<?xml version="1.0" encoding="utf-8"?>
<scale
xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:duration="200"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.05"
android:toYScale="1.05"
android:pivotX="50%"
android:pivotY="50%"
android:repeatMode="reverse"
android:fillAfter="false"
android:repeatCount="24"
/>
Unfortunately, this is limited to only one animation at once.
We can not define a sequence of animations this way...
You should include the attribute
android:repeatCount="infinite"
But in your "scale" animation not in "set"
To get a repeating animation I utilized the animation listener, and called the animation again when it ended. This does a camera reticule focusing like animation with brackets.
Here is the animation layout xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:fromXScale="1.0"
android:toXScale=".7"
android:fromYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:toYScale=".7"
android:duration="1000"/>
<scale
android:duration="1000"
android:fromXScale=".7"
android:toXScale="1.0"
android:fromYScale=".7"
android:pivotX="50%"
android:pivotY="50%"
android:toYScale="1.0"
android:startOffset="1000"/>
</set>
Here is the java code
public void startAnimation() {
View brackets = findViewById(R.id.brackets);
brackets.setVisibility(View.VISIBLE);
Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(Animation arg0) {
Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
anim.setAnimationListener(this);
brackets.startAnimation(anim);
}
#Override
public void onAnimationRepeat(Animation arg0) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation arg0) {
// TODO Auto-generated method stub
}
});
brackets.startAnimation(anim);
}
I was also facing the same problem..
i included android:repeatCount="infinite" in XMl file..now its working fine...
<translate
android:fromXDelta="0"
android:toXDelta="80"
android:duration="1000"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:pivotX="50%"
android:pivotY="50%"
android:fillAfter="true"/>
you can try this code.
In your code just add,
firstAnimation.setRepeatCount(5);
This will repeat the animation for a definite time
firstAnimation.setRepeatCount(Animation.INFINITE);
firstAnimation.setRepeatMode(Animation.INFINITE);
This will repeat the animation indefinitely.
I tried to use Daniel's code to show animation exact number of times and had a problem: animation was shown approximatily n / 2 times, when n times expected.
So I have modified Daniel's code:
//...
#Override
public void onAnimationEnd(Animation arg0) {
mCurrentCount++;
if (mCurrentCount < REPEAT_COUNT) {
Animation anim = AnimationUtils.loadAnimation(BuzzFinderActivity.this, R.anim.crosshair_focusing);
anim.setAnimationListener(this);
brackets.post(new Runnable() {
#Override
public void run() {
brackets.startAnimation(anim);
}
}
}
}
//...
Using variant, shown above, animation is shown exectly REPEAT_COUNT times, because View.post() method gives an ability to start new animation after finishing all actions, related with previous animation.
you have to add just one line in your xml code that i suggested below .
<scale
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.2"
android:toYScale="1.2"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite" // just add this one line
android:fillAfter="false"
/>
</set>
I solved this problem using android:repeatMode="reverse" before in my project.
<scale
android:interpolator="#android:anim/decelerate_interpolator"
android:duration="500"
android:fromXScale="1.0"
android:fromYScale="1.0"
android:toXScale="1.2"
android:toYScale="1.2"
android:pivotX="50%"
android:pivotY="50%"
android:repeatMode="reverse"
android:repeatCount="infinite" />
With android sdk version 4.0.3:
In the given animation elements:
android:repeatCount="-1"
makes it an infinite animation.
Add the following class to your project:
import android.view.View;
import android.view.animation.Animation;
public class AnimationRepeater implements Animation.AnimationListener
{
private View view;
private Animation animation;
private int count;
public AnimationRepeater(View view, Animation animation)
{
this.view = view;
this.animation = animation;
this.count = -1;
}
public AnimationRepeater(View view, Animation animation, int count)
{
this.view = view;
this.animation = animation;
this.count = count;
}
public void start()
{
this.view.startAnimation(this.animation);
this.animation.setAnimationListener(this);
}
#Override
public void onAnimationStart(Animation animation) { }
#Override
public void onAnimationEnd(Animation animation)
{
if (this.count == -1)
this.view.startAnimation(animation);
else
{
if (count - 1 >= 0)
{
this.animation.start();
count --;
}
}
}
#Override
public void onAnimationRepeat(Animation animation) { }
}
For infinite loop of your view, do the following:
Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a).start();
If you want to repeat the animation for N-times only, do the following:
Animation a = AnimationUtils(Context, R.anim.animation);
new AnimationRepeater(View, a, int N).start();
N stands for number of repetitions.
I do most of my stuff programmatically and I may be late or inefficient on this one but this but I completed the repeat animationset goal (I a even have 2 alternating animation sets). All this code does is simply fade in one image,pause, then fade out, fade in another image, pause, fade out, and bring back the first one (rinse and repeat). I first defined my Imageviews:
final ImageView purple = (ImageView)findViewById(R.id.purp);
final ImageView yellow = (ImageView)findViewById(R.id.yell);
purple.setVisibility(View.INVISIBLE);
yellow.setVisibility(View.INVISIBLE);
Then I made two timers, task timers and handlers to deal with when to start and stop each animation:
Timer p = new Timer();
TimerTask pu = new TimerTask() {
public void run() {
handler1.post(new Runnable() {
public void run()
{
fadein(purple);
}
});
}};
p.schedule(pu, 6000, 12000);
final Handler handler2 = new Handler();
Timer y = new Timer();
TimerTask ye = new TimerTask() {
public void run() {
handler2.post(new Runnable() {
public void run()
{
fadein(yellow);
}
});
}};
y.schedule(ye, 0, 12000);
Finally, rather than creating animation sets by adding animations, I just animations listeners to to determine when to start each animation:
public void fadein (final ImageView image)
{
Animation anim = new AlphaAnimation(0, 1);
anim.setDuration(2000);
image.startAnimation(anim);
anim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation)
{
image.clearAnimation();
image.invalidate();
pause(image);
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
});
}
public void pause (final ImageView image)
{
Animation anim = new AlphaAnimation(1, 1);
anim.setDuration(2000);
image.startAnimation(anim);
anim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation)
{
image.clearAnimation();
image.invalidate();
fadeout(image);
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
});
}
public void fadeout (final ImageView image)
{
Animation anim = new AlphaAnimation(1,0);
anim.setDuration(2000);
image.startAnimation(anim);
anim.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation animation)
{
image.clearAnimation();
image.invalidate();
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
});
}
The clearanimation and invalidate where just previous attempts and getting this thing to work right. I don't know if they are required or not.
Hope this helps someone.
Ryan
i got this to go ...i was trying to get a view to rotate in a circle continuously .
previous i was using rotation.setRepeatMode(-1) but that didn't work. switched to setrepeatcount and it works. This is on jelly bean 4.2.2
ObjectAnimator rotation = ObjectAnimator.ofFloat(myview,
"rotation", 360).setDuration(2000);
rotation.setRepeatMode(-1);
rotation.setRepeatCount(Animation.INFINITE);
rotation.start();
I've faced the same problem, but didn't want to do any timing things in Java because of the point that the UI thread may be very busy sometimes.
The INFINITE flag doesn't work for the set tag. So I resolved the issue with a little piece of code:
mAnimation = (AnimationSet) AnimationUtils.loadAnimation(myContext, R.anim.blink);
mIcon.startAnimation(mAnimation);
mAnimation.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
mIcon.startAnimation(mAnimation);
}
});
with the following XML:
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0" />
<alpha
xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="0.9"
android:startOffset="1000"
android:toAlpha="0.0" />
Where mIcon is an ImageView from my layout.
I've solved this problem. This is my version of the fix:
public class HelloAndroidActivity extends Activity {
private static String TAG = "animTest";
private Animation scaleAnimation;
private int currentCover = 0;
private List<ImageView> imageViews = new ArrayList<ImageView>(3);
private Button btn;
private ImageView img;
/**
* Called when the activity is first created.
* #param savedInstanceState If the activity is being re-initialized after
* previously being shut down then this Bundle contains the data it most
* recently supplied in onSaveInstanceState(Bundle). <b>Note: Otherwise it is null.</b>
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i(TAG, "onCreate");
setContentView(R.layout.test);
img = (ImageView)findViewById(R.id.testpict);
imageViews.add(img);
img = (ImageView)findViewById(R.id.testpictTwo);
imageViews.add(img);
img = (ImageView)findViewById(R.id.testpict3);
imageViews.add(img);
scaleAnimation = AnimationUtils.loadAnimation(this, R.anim.photo_scale);
scaleAnimation.setAnimationListener(new CyclicAnimationListener());
btn = (Button)findViewById(R.id.startBtn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imageViews.get(0).startAnimation(scaleAnimation);
}
});
}
private class CyclicAnimationListener implements AnimationListener{
#Override
public void onAnimationEnd(Animation animation) {
currentCover += 1;
if(currentCover >= imageViews.size()){
currentCover = 0;
}
img = imageViews.get(currentCover);
scaleAnimation = AnimationUtils.loadAnimation(HelloAndroidActivity.this, R.anim.photo_scale);
scaleAnimation.setAnimationListener(new CyclicAnimationListener());
img.startAnimation(scaleAnimation);
}
#Override
public void onAnimationRepeat(Animation animation) {
Log.d("Animation", "Repeat");
}
#Override
public void onAnimationStart(Animation animation) {
}
}
}
i just came across this issue while working on a backwards compatible app. so frustrating! i ended up coding a nice workaround class that can be called from onCreate and will kickoff any animation resource into an indefinite loop.
the class, AnimationLooper, is available here:
https://gist.github.com/2018678
After researching through the answers from internet, I found a solutions which works perfectly for me. (And yes, the repeatCount and repeatMode is extremely buggy when used together with animationSet).
anim_rotate_fade.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="#android:anim/accelerate_decelerate_interpolator"
android:ordering="together" >
<objectAnimator
android:duration="3000"
android:propertyName="rotation"
android:repeatCount="1"
android:valueTo="360"
android:valueType="floatType" />
<objectAnimator
android:duration="3000"
android:propertyName="alpha"
android:repeatCount="1"
android:repeatMode="reverse"
android:valueFrom="0.0"
android:valueTo="0.3"
android:valueType="floatType" />
<objectAnimator
android:duration="3000"
android:propertyName="y"
android:repeatCount="1"
android:repeatMode="reverse"
android:valueFrom="380"
android:valueTo="430"
android:valueType="floatType" />
</set>
In activity:
(Solve it by introducing a slight delay after animation ended).
ImageView starlightImageView = new ImageView(this);
starlightImageView.setImageResource(R.drawable.starlight);
final AnimatorSet animate = (AnimatorSet) AnimatorInflater.loadAnimator(this, R.anim.anim_rotate_fade);
AnimatorListenerAdapter animatorListener = new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
new Handler().postDelayed(new Runnable() {
#Override public void run() {
animate.start();
}
}, 1000);
}
};
animate.setTarget(starlightImageView);
animate.addListener(animatorListener);
There are a lot of classes you would like to research on, but currently I'm using objectAnimator which is highly flexible. I wouldn't recommend to use Animation or AnimationUtils:
Animation
AnimationUtils
Animator
AnimatorInflater
AnimatorListener
AnimatorListenerAdapter
One need to listen for completion of first animation then re-start the animation in onStopAnimation call back, give a try to this link
Little tweak to #Danufr answer to save resources from loading again.
operator = (ImageView) findViewById(R.id.operator_loading);
final Animation ani = AnimationUtils.loadAnimation(getApplicationContext(),R.anim.finding_operator);
ani.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
operator.startAnimation(ani);
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
operator.setAnimation(ani);
I solved this problem using thread.
Button btn = (Button) findViewById(R.id.buttonpush);
final TextView textview = (TextView) findViewById(R.id.hello);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
textview.setText("...................");
final Animation animationtest = AnimationUtils.loadAnimation(MainActivity.this, android.R.anim.slide_in_left);
animationtest.setDuration(1000);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
public void run() {
handler.postDelayed(this, 1500);
textview.startAnimation(animationtest);
}
};
handler.postDelayed(runnable, 500); // start
handler.removeCallbacks(runnable); //STOP Timer
}
});
it's working fine
GifDrawable gifDrawable = (GifDrawable) gifImageView.getDrawable();
gifDrawable.setLoopCount(0);
None of the above solutions worked in my case. The solution by Danuofr did work for animation set but when I was doing unit testing, my tests used to get stuck in this infinite loop. Finally, specific to my case, I needed to repeat this animation specific number of times. So, I manually added copies of my animation in anim_rot.xml in a cascaded manner adding the offset value. I know it's bad and won't work for many but it was the only workaround for my case.
anim_rot.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="2000"
android:fromDegrees="20"
android:pivotX="29%"
android:pivotY="50%"
android:toDegrees="-20" />
<rotate
android:duration="2000"
android:fromDegrees="-20"
android:pivotX="29%"
android:pivotY="53%"
android:startOffset="2000"
android:toDegrees="20" />
<rotate
android:startOffset="4000"
android:duration="2000"
android:fromDegrees="20"
android:pivotX="29%"
android:pivotY="56%"
android:toDegrees="-20" />
<rotate
android:duration="2000"
android:fromDegrees="-20"
android:pivotX="29%"
android:pivotY="59%"
android:startOffset="6000"
android:toDegrees="20" />
<rotate
android:startOffset="8000"
android:duration="2000"
android:fromDegrees="20"
android:pivotX="29%"
android:pivotY="62%"
android:toDegrees="-20" />
<rotate
android:duration="2000"
android:fromDegrees="-20"
android:pivotX="29%"
android:pivotY="65%"
android:startOffset="10000"
android:toDegrees="20" />
</set>
I did this to repeat the animation 3 times. You can add more copies to repeat it specific times by adding offset values.
Try to add the code to a looping thread or a while/for statement

Categories

Resources