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
}
});
Related
My app plays a few seconds wav song in a loop through MediaPlayer.setLooping(true), and I want an indicator (a simple 3dp width view) that moves on the screen showing the progress, starting over at the left when the song restarts in the loop.
One way of doing this is through a HandlerTask like this:
mHandlerTask = new Runnable() {
#Override
public void run() {
if (mms != null && mms.isPlaying()) {
playprogress.setTranslationX(playprogresswidth * mms.getCurrentPosition() / duration);
}
mHandler.postDelayed(this, 500);
}
This works, but the movement is not continuous, with jumps every 500ms. Of course I can choose 50, or 5, or 1, instead 500, but then that should be quite heavy and a battery drainer.
So I implemented a ValueAnimator, like this:
ValueAnimator animation = ObjectAnimator.ofFloat(playprogress, "translationX", playprogresswidth);
animation.setRepeatCount(ValueAnimator.INFINITE);
animation.setRepeatMode(ValueAnimator.RESTART);
animation.setDuration(duration);
animation.setInterpolator(new LinearInterpolator());
animation.start();
This works, except for the fact that the MediaPlayer and the animation are not perfectly synced, and go out of sync more and more as the loop restarts. And this actually makes sense: the MediaPlayer needs some extra time for the restart.
So how I can sync both? I could not find any listener in MediaPlayer to trigger a single animation for each loop. I tried with an animation.addListener(new Animator.AnimatorListener() {...}) to try to fiddle in the duration without much luck. This is the best I got so far, with a non-repeat animation (mms is the MediaPlayer):
ValueAnimator animation = ObjectAnimator.ofFloat(playprogress, "translationX", playprogresswidth);
animation.setDuration(duration+150);
animation.setInterpolator(new LinearInterpolator());
animation.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {}
#Override
public void onAnimationEnd(Animator animation) {
if (mms != null && mms.isPlaying()) {
animation.setDuration(duration - mms.getCurrentPosition() + 150);
animation.start();
}
}
#Override
public void onAnimationCancel(Animator animation) {}
#Override
public void onAnimationRepeat(Animator animation) {}
});
animation.start();
The problem with this approach is that 150: it should be device independent.
EDIT
I found a much better solution, although probably not the best one yet:
playprogress.setTranslationX(0);
ValueAnimator animation = ObjectAnimator.ofFloat(playprogress, "translationX", playprogresswidth);
animation.setInterpolator(new LinearInterpolator());
animation.setDuration(duration);
animation.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
if (mms != null && mms.isPlaying()) {
animation.setStartDelay(duration-mms.getCurrentPosition());
animation.start();
}
}
});
animation.start();
I placed an ImageView in the centre of the screen. I then wanted to animate it from the bottom to the centre. I reasoned to move it without delay out of the screen and then back the same value which would end in the centre. However, the animation makes it so the view will exit from top.
mLogoIV.animate().translationY(1000f).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mLogoIV.animate().translationY(-1000f).setDuration(3000);
}
});
use translationYBy( to animate relative to the current position
https://developer.android.com/reference/android/view/ViewPropertyAnimator.html#translationYBy(float)
mLogoIV.animate().translationYBy(1000f).setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
mLogoIV.animate().translationYBy(-1000f).setDuration(3000);
}
});
From the docs:
The amount to be animated by, as an offset from the current value.
vs what you are doing now:
The value to be animated to.
Use Object animator.
ObjectAnimator objectAnimator= ObjectAnimator.ofFloat(mLogoIV, "translationY", -750, 0);
objectAnimator.setDuration(1000);
objectAnimator.start();
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();
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.
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);
}
});