Am using an ObjectAnimator to translate the text in a text view similar to a stock ticker. I want to pause the ticker on press of a button. However, inspite of calling cancel()/end() on the animator object, the ticker continues to move till the end of the animation.
Have also tried explicitly clearing all animations on the underlying textView in the AnimatorListerner#onAnimCancel(). However the underlying textView doesn't seem to be associated with any animations.
Would be great if someone could point out as to what would be going wrong.
Relevant sections of code as below.
anim = ObjectAnimator.ofFloat(mTextViewTicker, "TranslationX", 0, -mSomeValue);
anim.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationCancel(Animator animation) {
Log.d(TAG,"Animation Pause invoked");
super.onAnimationCancel(animation);
}
public void onAnimationEnd(Animator animation) {
Log.d(TAG,"Animation Ended");
((View)((ObjectAnimator)animation).getTarget()).setTranslationX(anim.getAnimatedFraction());
}
});
anim.start();
public void stopScroll() {
anim.cancel();
}
stopScroll() is invoked from a button click in the activity.
Related
I have a "track" that goes for 50 "lenghts" and I have imageView that changes position on click for 5 "lenghts" at a time. That transition I handle with animation like this:
anima = ObjectAnimator.ofFloat(bar, "translationX", position*pix);
anima.setDuration(500);
anima.start();
At the end of the track I have another imageview that I want to detect collision with so I after animation I do this:
anima.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if (Rect.intersects(n1, barRect)) {
//Stuff after collision
}
}
My question is, is there a way to detect collision during this animation so I stop it if collision is somewhere between those "5 lenght jumps"
Thanks to #NikolaDespotski I've managed to solve the problem by implementig onUpdateListener for my ObjectAnimator object like this:
anima.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
//Do collision detection here
}
});
probably another rhetorical question.
In iOS when we set a view's (any any UIView subclass such as UIButton) alpha to 0, iOS by default disables all user interaction on that view.
I got an Android app where I animate fade out the view by:
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(buttonSelectionContainer, "alpha", 1, 0);
fadeOut.setDuration(500);
fadeOut.start();
However, I am noticing that when I tap the screen, the animation starts again, leading me to believe, in Android, even when a button alpha is set to 0, it is still tappable, is this true?
Is there a way to globally tell Android to disable user interaction for a view (and all its subviews) when its alpha is set to 0, either explicitly through using:
view.setAlpha(0.0f);
or through the ObjectAnimator like the above code block I used ?
A temporary work around for my problem would probably be to schedule this code to run after 500 ms:
// psuedocode: after 500ms
dispatch_doSomethingAfter(500)
{
myButton.setEnabled(false);
}
Not the ideal solution but might be my only solution, unless some bright Android developers out there has a better solution ?
Use addListener on your ObjectAnimator to control what happens after the animation has finished.
fadeOut.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
button.setEnabled(false);
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
You can create an Animator.AnimatorListener that automatically disables the target View when the animation ends.
Declare your custom DisableViewOnEndAnimatorListener class:
public class DisableViewOnEndAnimatorListener extends AnimatorListenerAdapter {
#Override
public void onAnimationEnd(Animator animation) {
if (animation instanceof ObjectAnimator) {
final Object target = ((ObjectAnimator) animation).getTarget();
if (target instanceof View) {
((View) target).setEnabled(false);
}
}
}
}
Then, in your code:
DisableViewOnEndAnimatorListener endAnimatorListener = new DisableViewOnEndAnimatorListener();
ObjectAnimator button1FadeOut = ObjectAnimator.ofFloat(button1, "alpha", 1, 0);
button1FadeOut.setDuration(500);
button1FadeOut.addListener(endAnimatorListener);
button1FadeOut.start();
ObjectAnimator button2FadeOut = ObjectAnimator.ofFloat(button2, "alpha", 1, 0);
button2FadeOut.setDuration(500);
button2FadeOut.addListener(endAnimatorListener);
button2FadeOut.start();
I applied an infinite animation on an ImageView to indicate a running background thread in my app. When the thread finishes, I'm able to stop the animation by using clearAnimation(), but it snaps the ImageView back to its starting position, and I'd like the current animation cycle to complete (which is designed to gracefully end in its starting position). Is there a way to do this?
Register an AnimationListener, and wait until onAnimationRepeat() to clear it. I haven't tried this, but I think it will work.
Just call setRepeatCount(0) and then listen for onAnimationEnd. See here for more details.
You can set the desirable position of your animation in the onAnimationEnd() method of your animation listener. Then, instead of going to the initial position, the View will be displayed at the coordinates you set up there.
animation = new TranslateAnimation(0, -length, 0, 0); //setup here your translation parameters
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(10);
animation.setAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {
animationRunning = true;
}
public void onAnimationRepeat(Animation animation) {
}
public void onAnimationEnd(Animation animation) {
animationRunning = false;
// setAnimationPositionAfterPositionChange();
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) runningText.getLayoutParams();
params.leftMargin = currentPosition; //Calculate you current position here
runningText.setLayoutParams(params);
}
});
runningText.setAnimation(animation);
If you are using animate()
you use setListener(null) to stop it the animation,
works for me.
image.animate()
.translationXBy(-width* 0.5f)
.setDuration(300)
.setStartDelay(6000)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
image.animate().rotationBy(90).setDuration(1000).setStartDelay(1).setListener(null);
}
});
I've got a PopupWindow that's using an alpha animation to produce a fade-in display of the window.
Using PopupWindow.setAnimationStyle() works as expected: the popup fades-in when shown.
However, once the popup is displayed (meaning the fade-in animation has completed) I'd like to kick off another animation.
I've tried using the following to obtain the underlying animation that's referred to via setAnimationStyle() and attach an AnimationListener to it:
Animation fadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.popup_fade_in);
fadeInAnimation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
Log.d(TAG, "fade-in animation START");
}
#Override
public void onAnimationEnd(Animation animation) {
Log.d(TAG, "fade-in animation END");
// Kick off the next animation
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
This does't work: None of the AnimationListener methods ever get called.
Anyone know of a way to determine when a popup window's animation has ended?
Alternatively, if there's a way to determine when the popup window has initially become visible I could kick off the secondary animation at that time. Unfortunately, I'm not finding anything in the API docs indicating how to do this.
Appreciate the help!
It seems that an android animation is not truly finished when the onAnimationEnd event is fired although animation.hasEnded is set to true.
I want my view to change it's background drawable on the end of it's ScaleAnimation which it does, but you can clearly see that it is changed some miliseconds before it finishes. The problem is, that it flickers because the new background appears (=is) scaled for a short time until the animation really finishes.
Is there a way to get either the real end of the animation or just prevent the new background from beeing scaled this short period of time?
Thank you!
//EDIT: I'm using an AnimationListener to get the following call:
#Override
public void onAnimationEnd(Animation animation)
{
View view = (MyView) ((ExtendedScaleAnimation) animation).getView();
view.clearAnimation();
view.requestLayout();
view.refreshBackground(); // <-- this is where the background gets changed
}
Here is the actual bug related to this issue http://code.google.com/p/android-misc-widgets/issues/detail?id=8
This basically states that the onAnimationEnd method doesn't really work well when an AnimationListener is attached to an Animation
The workaround is to listen for the animation events in the view to which you were applying the animation to
For example if initially you were attaching the animation listener to the animation like this
mAnimation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationEnd(Animation arg0) {
//Functionality here
}
});
and then applying to the animation to a ImageView like this
mImageView.startAnimation(mAnimation);
To work around this issue, you must now create a custom ImageView
public class MyImageView extends ImageView {
and then override the onAnimationEnd method of the View class and provide all the functionality there
#Override
protected void onAnimationEnd() {
super.onAnimationEnd();
//Functionality here
}
This is the proper workaround for this issue, provide the functionality in the over-riden View -> onAnimationEnd method as opposed to the onAnimationEnd method of the AnimationListener attached to the Animation.
This works properly and there is no longer any flicker towards the end of the animation.
I was abe to resolve this by calling clearAnimation() on the view being animated inside onAnimationEnd, that took away the flicker
Its weird why would anyone have to do that, as onAnimationEnd callback should have been called only if the animation has already ended. But I guess the answer lies in the depth of Framework on how view/layout handles animation callback.
For now take it as a hack-free solution, that just works.
animation.setAnimationListener(new AnimationListener() {
public void onAnimationEnd(Animation anim) {
innerView.clearAnimation(); // to get rid of flicker at end of animation
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams
(innerBlockContainer.getWidth(), innerBlockContainer.getHeight());
/* Update lp margin, left/top to update layout after end of Translation */
ViewGroup parent_ofInnerView = (ViewGroup)innerView.getParent();
vp.updateViewLayout(innerBlockContainer, lp);
}
public void onAnimationRepeat(Animation arg0) {}
public void onAnimationStart(Animation arg0) {
}
});
innerView.startAnimation(animation);
I had same issue and solved it using
view.clearAnimation();
before
view.startAnimation(anim);
I had a similar problem and I used Soham's solution with custom view class.
It worked fine, but at the end, I've found a simpler solution that worked for me.
After calling the view.StartAnimation(animation), and before the next step in my program, I've added a short delay that will be long enough to let the animation finish, but short enough to be unnoticeable by the user:
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
nextStepInMyProgram();
}
}, 200);// delay in milliseconds (200)
For some reason the onAnimationStart works properly, and the onAnimationEnd doesnt. So heres how I originally did it and what I changed:
Attempt 1 (flicker):
a) Move image from 0px to 80px
b) In onAnimationEnd, set the image's location to 80px
Attempt 2 (no flicker):
a) In onAnimationStart, set the image's location to 80px
b) Move the image from -80px to 0px
Hope that made sense. Basically I flipped the way I did it
Try to use getAnimation() from your object:
public void onShowListBtnClick(View view)
{
rightPanel.startAnimation(AnimationUtils.loadAnimation(MainActivity.this, R.anim.slide_left));
rightPanel.getAnimation().setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
#Override
public void onAnimationEnd(Animation animation) {
// write your code here
}
});
}
An easy fix is to add one line to AnimationListener.onAnimationEnd():
#Override
public void onAnimationEnd(Animation a) {
a.setAnimationListener(null);
…
}
annimation can be also stopped on screen rotation. in this case onAnimationEnd() is not being called. my workaround:
animation.setDuration(animationDuration);
animation.setAnimationListener(new AnimationListenerAdapter() {...});
view.startAnimation(animation);
handler.postDelayed(new Runnable() {
#Override
public void run() {
if(!animation.hasEnded()) {
// here you can handle this case
}
}
}, animationDuration + 100);
I had this issue because my Animation was not started in the main thread.
This resulted in a duration of 0.
However , the Animation did play correctly - it just called onAnimationEnd() immediately after execution.
If you are using repeat count as infinite, then onAnimationEnd would not get called. Check the documentation link
You can also use setUpdateListener, then check the current fraction of the animation progress and act accordingly.
Here's a Kotlin example for a fade-out animation which eventually makes the view gone from the layout:
view.animate()
.alpha(0f)
.setDuration(duration)
.setUpdateListener { animation ->
if (animation.animatedFraction > 0.99f) {
view.visibility = View.GONE
}
}
.start()
This worked for me:
#Override
public void onAnimationUpdate(ValueAnimator animation) {
if (Float.compare(animation.getAnimatedFraction(),1.0f)) {
// animation ended;
//do stuff post animation
}
}