I'm trying to swap two images in separate Imageviews with a fade-out/fade-in. The first image fades out, then the other image fades out, then the second image fades in while replaced with the first image, then the first image fades back in while being replaced with the second image (swap images with fade out/in). The code is below. The swap is occurring okay but there is no fade out or in. What am I missing to get the alpha fade to occur? Also, if I use an integer (such as 1500) instead of mShortAnimationDuration, the animationlistener does not get triggered. setDuration argument is type long. I based my code on the example from here: https://developer.android.com/training/animation/crossfade.html
Drawable target_draw = target.getDrawable();
Drawable dragged_draw = dragged.getDrawable();
final Bitmap targetBitmap = ((BitmapDrawable)target_draw).getBitmap();
final Bitmap draggedBitmap = ((BitmapDrawable)dragged_draw).getBitmap();
//SWAP and CROSSFADE ANIMATION
dragged.setAlpha(1f);
target.setAlpha(1f);
int mShortAnimationDuration = getResources().getInteger(android.R.integer.config_shortAnimTime);
dragged.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
dragged.setImageBitmap(targetBitmap);
dragged.invalidate();
}
});
target.animate()
.alpha(0f)
.setDuration(mShortAnimationDuration)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
target.setImageBitmap(draggedBitmap);
target.invalidate();
}
});
//fade in with new images
target.animate()
.alpha(1f)
.setDuration(1500);
dragged.animate()
.alpha(1f)
.setDuration(1500);
UPDATE:
I finally came up with the following code which basically works, but due to the fact that Android is not a real-time OS, and that fact that I am swapping images and calling invalidate() during the fade transitions, makes the timing of the fades somewhat unpredictable.
dragged.animate()
.alpha(0f)
.setDuration(1000)
//.setInterpolator(new DecelerateInterpolator()) //optional
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
dragged.setImageBitmap(targetBitmap);
dragged.invalidate();
dragged.animate()
.alpha(1f)
//.setInterpolator(new AccelerateInterpolator())
.setDuration(1000);
//.setStartDelay(500); //also optional for timing
}
});
target.animate()
.alpha(0f)
.setDuration(1000)
.setStartDelay(500)
//.setInterpolator(new DecelerateInterpolator())
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
target.setImageBitmap(draggedBitmap);
target.invalidate();
target.animate()
.alpha(1f)
//.setInterpolator(new AccelerateInterpolator())
//.setStartDelay(500)
.setDuration(1000);
}
});
...update continued:
Since the timing of the fades were inconsistent due to image swapping during them, I simplified and eliminated the fade-out, and just kept the image swap and fade-in. This is the code I ended up with. I am looking for more interesting ways to animate Imageview swapping if anyone has any ideas on that.
// swap
dragged.setImageBitmap(targetBitmap);
dragged.invalidate();
target.setImageBitmap(draggedBitmap);
target.invalidate();
// then fade-in with delay
Animation fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
Animation fadeIn2 = new AlphaAnimation(0, 1);
fadeIn2.setInterpolator(new DecelerateInterpolator());
fadeIn2.setStartOffset(300);
fadeIn.setDuration(2000);
fadeIn2.setDuration(2000);
target.startAnimation(fadeIn);
dragged.startAnimation(fadeIn2);
Related
I have an ImageView with alpha=0 by default. So it acts as an invisible overlay on another image.
On click, I want to create an animation that shows the overlay image for 200ms, and then hides it again.
The following does work in general, but only a single time! Why?
final ImageView flash = (ImageView) view.findViewById(R.id.flash);
flash.animate()
.alpha(255) //make visible
.setDuration(200)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
flash.setImageAlpha(0); //hide again
}
});
you're setting 2 different alpha values, the first one is View's alpha, but when you end the animation you set the ImageView class alpha to 0, so if you start the animation again, the View alpha is 1.0f but the image alpha will be 0 and you see nothing.
change it to
flash.animate()
.alpha(1.f)
.setDuration(200)
.setListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
flash.setAlpha(0.f);
}
});
View (and therefore ImageView) has a method setAlpha(float), ImageView adds also another method setAlpha(int), which is deprecated because it's confusing as hell and now it's been renamed to setImageAlpha(int). the animation will call View's setAlpha(float)
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();
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
}
});
I have an Activity with a toolbar (which is part of the SharedElements Activity entering animation) and below that toolbar are three ImageViews horizontally next to each other. In their XML implementation all three are set INVISIBLE.
What I'm trying to do is, to animate them sequentially "dropping" from behind the toolbar. My implemenation is this:
int delay = 500;
for (int y = 0; y < 3; y++) {
ObjectAnimator oa = ObjectAnimator.ofFloat(imageViews[y],
"translationY", -300, 0);
oa.setDuration(600);
oa.setStartDelay(delay);
oa.start();
imageViews[y].setVisibility(View.VISIBLE);
delay = delay+100;
}
}
As you can see, I'm iterating through the three ImageViews and start a animation for each one to go from a -300 X-position (which is behind the toolbar) to their normal position.
This animation works great - just as I want it to be, but the problem is, that right before all ImageViews are briefly flickering which I can't explain. I tried debugging, but while I'm going through the lines of that part my screen stays black. So I can't determine where/why the Views become visible.
Maybe you can help me to find my mistake.
Thank you, this is my working Code:
For all three ImageViews:
ObjectAnimator anim1Pin = ObjectAnimator.ofFloat(img_pinned, "translationY", -300, 0);
anim1Pin.setDuration(ANIMATON_DURATION);
anim1Pin.setStartDelay(300);
anim1Pin.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
img_pinned.setVisibility(View.VISIBLE);
}
#Override
public void onAnimationEnd(Animator animation) {
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
And the AnimatorSet:
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(anim1Pin, anim2Alarm, anim3LED);
animatorSet.start();
Few things, first, the problem may be as simple as setting the visiblity state to GONE and then after animation starts, setting it to visible. However, I would also use AnimatorSet to play the animations together and add the delay rather than do it in a loop.
If you use AnimatorSet there is an onAnimationStart method in AnimationListener that you can use the set the visible to VISIBLE rather than do it how you have to ensure that they become visible at the right time.
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);
}
});