I have to animate a circular count down timer and I'm doing it by animating the background of an ImageView using AnimationDrawable (each image has the according slice of the circle removed). The problem is that the user has the ability to pause this animation, so I have to reload the animation but then a problem appears. I've tried setting the the animation to null, setting the background of the ImageView to null, setting the visibility of the animation, but practically nothing helped, because number of frames remains the same. I have to find a workaround for deleting all frames and adding new ones or to reset the whole animation to the default state.
Step 1 - initializing the animation (starting frame index is 0)
timerView.setBackgroundResource(R.drawable.timer_switch);
timerAnimation = (AnimationDrawable)timerView.getBackground();
System.out.println("Number of frames: " + timerAnimation.getNumberOfFrames());
for (int frameIndex = startingFrameIndex; frameIndex < 60; frameIndex++) {
if (frameIndex < 10) {
uri = "drawable/timer_0000" + frameIndex;
} else {
uri = "drawable/timer_000" + frameIndex;
}
imageResourceId = getResources().getIdentifier(uri, null, getPackageName());
timerAnimation.addFrame(getResources().getDrawable(imageResourceId), timerImageFrameDuration);
}
Step 2 - here's the tricky part where I don't know what to do. Things that I've tried:
timerAnimation.stop();
timerAnimation.setCallback(null);
timerAnimation.setVisibility(true, false);
timerAnimation = null;
Step 3 - after calling step 2 I call step 1 again, but the Sys.out still displays 60 as the current number of frames. (starting index here is set to the last hidden frame when pause button was tapped.)
Any idea is welcomed.
Thanks
This will send the (AnimationDrawable) timerAnimation to the first frame as soon as stop() has been called:
timerAnimation.stop();
timerAnimation.selectDrawable(0);
I had the same problem where stopping the animation would stop on the current frame. I wanted it to behave like iOS, where stopping would go back to the first frame. This solution works for me:
((AnimationDrawable)(someButton.getBackground())).stop();
someButton.setBackgroundDrawable(null);
someButton.setBackgroundResource(R.drawable.animation);
This first stops it (probably not necessary). Then it kills the background animation. Finally, it recreates the background.
Another possible solution would be to have the same image for the first frame and last frame and do the following instead of calling the stop() method.
((AnimationDrawable)(someButton.getBackground())).setOneShot(true);
if anyone does look into this any further. There is also a method called setVisible(boolean visible, boolean restart). However, that did not work for myself.
You can try public boolean setVisible (boolean visible, boolean restart) and it will play the animation once. For example:
animationDrawable.setVisible(false, true);
I would setVisibility(true, true):
timerAnimation.stop();
timerAnimation.setVisibility(true, true);
visible boolean: true if visible, false otherwise
restart boolean:
when visible, true to force the animation to restart from the first frame
android.view.animation.Animation.reset() doesnt work?
Related
I have two ImageView(s) set in my layout one on top of the other, say A and B.
I have set the alpha of B to 0 so it's totally faded as I want to control which image is displayed by setting the appropriate alpha to 1 in my code.
Now I also want to rotate the Images by 360 degrees before they fade out and before the new image comes on the screen when I click on the ImageView(s).
I'm using the ViewPropertyAnimator's rotation() and alpha() methods to do this.
This works without any issues when the App is installed and runs for for 1st time.
However, next time when I click on the Images, only the fade-in and fade-out animations work.
The rotation does not work.
Check this code :
boolean isImage1Showing = true;
//onClick() method for the 2 ImageView(s)
public void fadeAndRotate(View view){
if (isImage1Showing){
//Rotate the resource on 1st ImageView by 360 degrees
imageViewA.animate().rotation(360f).setDuration(2000);
imageViewA.animate().alpha(0).setDuration(2000);
//Displaying the other image on to the screen
imageViewB.animate().alpha(1).setDuration(2000);
isImage1Showing = false;
} else {
isImage1Showing = true;
imageViewB.animate().rotation(360f).setDuration(2000);
//Displaying the previous image
imageViewA.animate().alpha(1).setDuration(2000);
//fading out the currently displayed image
imageViewB.animate().alpha(0).setDuration(2000);
}
}
Use the
.rotationBy(float value)
method instead of the
.rotation(float value)
method. In your case it rotates TO 360f but you want it to rotate BY 360f.
So this code should work :
imageViewA.animate().rotationBy(360f).setDuration(2000);
I am trying to do an animation whenever my character jumps. It works in the following way: when I jump, I first draw the animation of my char. If it is finished, I start modifying his position and also draw another texture, for the inAir moment. The problem is that the animation runs instantly (I tried putting the speed 1 frame per second,but it still runs instantly) and the character jumps before the animation is done. I suspect it considers it finished before it actually finishes. This is how I check if the animation is finished: first I check if the animation start (the jump button is pressed) and then I have an if condition like this:
if (animationStarted && rightJumpAnim.isAnimationFinished(System.currentTimeMillis()))
{
animationStarted = false;
animationFinished = true;
timePassed1 = 0;
}
After this, I put animationFinished to false when he touches the ground again. Any advice?
P.S. here is the code for running the jumpAnimation:
if (toDrawJumpRight1 == true) {
animationStarted = true;
timePassed1 += Gdx.graphics.getDeltaTime();
spriteBatch.draw(rightJumpAnim.getKeyFrame(timePassed1, false), posX + cameraX, posY + cameraY, sizeX, sizeY);
}
Your problem should be in the very first line you provided to us:
if (animationStarted && rightJumpAnim.isAnimationFinished(System.currentTimeMillis()))
The argument you must pass is the STATETIME of the animation.
if (animationStarted && rightJumpAnim.isAnimationFinished(timePassed1))
Edit:
Some more Informations to this problem:
You say you want 1 frame stay 1 Second.
Now we say, you have approx 5 frames.
5frames * 1 second = 5seconds for finish
so if statetime is over 5 seconds, your animation is played ONE time (is finished)
Animation.isAnimationFinished(float stateTime) checks now, if the current stateTime from your animation is higher than the 5 seconds.
System.currentTimeMillis is at this moment:
1440618906341
now.. whats bigger?
Have a nice Evening (I recommend using TimeUtils.millis instead of System.currentTimeMillis, guess it's optimated, fo' sure.)
I am aware that API level 19 supports pause() and resume() on ObjectAnimators. But in my project at API level 14, I have an ObjectAnimator which is applied to an Image view to rotate it. I want to pause the animation provided by the ObjectAnimator on touch and resume it from the place where the image view was (before touch down).
So I attempted to save the current play time and cancel the object animator on my stopAnimation() function.
private void stopAnimation(){
currentTime = mGlobeAnimator.getCurrentPlayTime();
mGlobeAnimator.cancel();
}
In the startAnimation() function, I recreate the animator, set its target to the image view, set the saved play time and start it.
private void startAnimation(Context context, View view, float startAngle) {
ObjectAnimator globeAnimatorClone = (ObjectAnimator)AnimatorInflater.loadAnimator(context, R.animator.rotate_globe);
globeAnimatorClone.setTarget(mImageView);
globeAnimatorClone.setCurrentPlayTime(currentTime);
globeAnimatorClone.start();
}
This does not work. Would anybody help please with any pointers to pause and resume animation provided by animator for API level before 19?
I think I got it working by starting the animator and then setting the currentPlayTime(). The documentation clearly tells (which I just stumbled upon) that if the animation has not been started, the currentPlayTime set using this method will not advance the forward!
Sets the position of the animation to the specified point in time. This time should be between 0 and the total duration of the animation, including any repetition. If the animation has not yet been started, then it will not advance forward after it is set to this time; it will simply set the time to this value and perform any appropriate actions based on that time. If the animation is already running, then setCurrentPlayTime() will set the current playing time to this value and continue playing from that point. http://developer.android.com/reference/android/animation/ValueAnimator.html#setCurrentPlayTime(long)
private void stopAnimation(){
mCurrentPlayTime = mRotateAntiClockwiseAnimator.getCurrentPlayTime();
mRotateAntiClockwiseAnimator.cancel();
}
private void startAnimation() {
mRotateAntiClockwiseAnimator.start();
mRotateAntiClockwiseAnimator.setCurrentPlayTime(mCurrentPlayTime);
}
What you are doing is that it will just restart your animation, instead you can create a custom class for animation with pause and resume method.
You need to first check if the device is 19 api and above if it is then use the native pauses and resume of the object animator else use the regular Animation designed from api 1, you can follow this thread for pause and resume below api 19.
Its probably too late for me to answer, i just came across this problem and i solved it using your method.
i just added two things to your approach
1. In the start of animation check if the ** mCurrentPlayTime** is greater than zero.if its >0 then setCurrentPlayTime. otherwise its useless.
2. when your animation ends make it zero again.
I am using ViewPagerparallax class,(for parallax view ) and I am setting the background using pager.setbackgroundAsset(....);
everything works fine....but I have a situation,what i want to achieve is that whenever the fragments go in a pause state and whenever it is resumed,the background should change and hence i am changing the background in the onResume method but seems like change is only visible whenever i try to swipe between fragments ...
this is my code
public void onResume()
{
Activity_Fragments ab = (Activity_Fragments) this.getActivity();
ViewPagerParallax pager=ab.hi();
int arr[]={R.raw.a1,R.raw.a2,R.raw.a3,R.raw.a4,R.raw.a5,R.raw.a6,R.raw.a7,R.raw.a8,
R.raw.a9,R.raw.a10,R.raw.a11,R.raw.a12,R.raw.a13,R.raw.a14,R.raw.a15,R.raw.a16,
R.raw.a17,R.raw.a18,R.raw.a19,R.raw.a20,R.raw.a21,R.raw.a22,R.raw.a23,
R.raw.a24,R.raw.a25,R.raw.a26,R.raw.a27,R.raw.a28,R.raw.a29};
int x=(int)(Math.random() * ((28 ) + 1));
pager.setBackgroundAsset(arr[x]);
A few notes.
First of, I am assuming you are using this: https://github.com/MatthieuLJ/ViewPagerParallax/blob/master/src/com/matthieu/ViewPagerParallax.java
When looking at the source it appears to be that you are doing things correctly.
I few things I can see that could be happening
The max number of pages set is 0, which is the default
The width or height is 0
The background hasn't changed from last time
The application has run out of memory (They say they don't want to use more then 1/5th of the memory
An exception was thrown while creating the bitmap of the image
A few things you could try
For point number 1, if you haven't already, call the set_max_pages with a number you see fit.
Point 2, Is the ViewPager in the activity with width and height set?
Point 3, confirm that you generator is working. (Already done, logic works perfect)
int arr[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,
18,19,20,21,22,23,24,25,26,27,28,29};
for(int i = 0; i < 100; i++){
Log.d("TEST", String.valueOf(arr[(int)(Math.random() * ((28 ) + 1))]));
}
This returns the desired output, no problem there.
Point 4 & 5, Check logcat, it could say some errors, the tag should say ViewPagerParallax
I would say point 1 could be it, it will draw the background if it hasn't already once you set it.
I'm not really sure how setBackgroundAsset works, but try a call to invalidate(); at the end of onResume(). That will insure that your onDraw() method will be called ASAP and the new background will be redrawn.
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.