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();
Related
This is not the first time i found this issue.
If you play an animation at the same time than another, it stops randomly.
halfPie.post(new Runnable() {
#Override
public void run() {
ValueAnimator animator = new ValueAnimator();
animator.setObjectValues(0, 100);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
halfPie.setCenterText(String.valueOf(animation.getAnimatedValue()));
}
});
animator.setDuration(10000); // here you set the duration of the anim
animator.start();
}
});
This animation is being interrupted by halfPie animation. (Pd: It has an animation itself that shows up the entire object from invisible to visible)
Note: I know you can listen to onAnimationEnd but i want them to play together. (That doesnt mean to use AnimationSet)
Put this line in Your application tag AndroidManifest.xml file
android:hardwareAccelerated="true"
I hope this will solve your Problem
In my app I'm using ValueAnimator - it fades one image into another. It works perfectly but only after launching app. When I want to animate images again animator is not working. I tried do use animation.end() in onAnimationEnd listener but app crashes. I tried everything. Can you please help me ? Here's my method to start animation
private void animate() {
if(animator != null)
animator.end();
animator = ValueAnimator.ofFloat(0f, 1f);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mPackOneSecond.setAlpha((Float) animation.getAnimatedValue());
mPackTwoSecond.setAlpha((Float) animation.getAnimatedValue());
mPackThreeSecond.setAlpha((Float) animation.getAnimatedValue());
}
});
animator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
startSorting(option);
}
});
animator.setDuration(2500);
animator.start();
}
Basically animate() in onCreate() works perfectly but when I want to restart it - it doesnt. Why ?
Call animator.cancel() before restart.
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);
}
});