Roldán Roldán - 1 month ago 13
Android Question

Android view.animate() works different and wrong after first execution

I've implemented a banner which pops out whenever the user losts connection. When is reconnected, another banner shows up for some seconds and disappears.

The layout is a RelativeLayout with a grey TextView for the warning*, and a green TextView for the reconnection message.

Example of both banners

If I just change Visibility.GONE and Visibility.VISIBLE on them, everything works fine.

But I wanted to do it fancier, so I added some animations. And I'm having a weird problem here. In first execution everything works as expected. Following executions don't work right.

I want to animate both textviews at the same time when reconnection happens. So the grey textview fades out, while the green textview fades in (both at the same time). This works like that only on first execution. Next times it executes secuentially: first the grey textview fades out, and after that, green textview fades in. And I don't want it secuentially.

I've uploaded a video to Youtube (30s) so you can see the effect. First working, and then not. It's easier to watch this than to read my description: https://youtu.be/rD1ZNzKen0U

Crossfade method is where all the magic happens. As you see, the view.animate() is called inside two threads. Initially I implemented exactly the same code, but without threads, because the animate() method should be asynchronous. But I'm getting nuts with this problem, so I tried do it like this. I've also tried to execute view.clearAnimations() before setting each animation. But nothing.

private void crossfade() {
final int animationDuration = 600;

// Set the view to 0% opacity but visible, so that it is visible (but fully transparent) during the animation.
bannerNetworkConnected.setAlpha(0f);
bannerNetworkConnected.setVisibility(View.VISIBLE);

bannerNetworkDisconnected.animate()
.alpha(0f)
.setDuration(animationDuration)
.setListener(null);

bannerNetworkConnected.animate()
.alpha(1f)
.setDuration(animationDuration)
// .setListener(null);
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dissapearFadding();
}
});

flipper.stopFlipping();
}


Method for dismissing the reconnected banner:

private void dissapearFadding() {
bannerNetworkConnected.animate()
.alpha(0f)
.setDuration(300)
.setStartDelay(1500)
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
bannerNetworkDisconnected.setVisibility(View.GONE);
container.setVisibility(View.GONE);
bannerNetworkConnected.setVisibility(View.GONE);
}
});
}


Network switching receiver:

public void networkStateChange(boolean connected) {
Log.i(MainActivity.TAG, "MainActivity.networkStateChange() " + connected);
//bannerNetworkDisconnected.setVisibility(connected ? View.GONE : View.VISIBLE);

if (bannerNetworkDisconnected != null && bannerNetworkConnected != null) {
bannerNetworkDisconnected.clearAnimation();
bannerNetworkConnected.clearAnimation();

if (!connected) {
appearFromTop();
} else if (connected && bannerNetworkDisconnected.getVisibility() == View.VISIBLE) {
crossfade();
} else {
container.setVisibility(View.GONE);
bannerNetworkConnected.setVisibility(View.GONE);
bannerNetworkDisconnected.setVisibility(View.GONE);
flipper.stopFlipping();
}
}
}


Banner XML:

<RelativeLayout
android:id="@+id/banner_network_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:visibility="gone">

<TextView
android:id="@+id/banner_network_disconnected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="No connection. Retrying..."
android:textColor="#F0F0F0"
android:visibility="gone"/>

<TextView
android:id="@+id/banner_network_connected"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#44bb44"
android:text="Reconnected!"
android:textColor="#F0F0F0"
android:visibility="gone"/>
</RelativeLayout>


I've created a new project and copied all the code for this, and the problem still there. If you want to inspect the whole classes here, you have the whole project on github:
https://github.com/rolgalan/NetworkBanner

I really would appreciate any help on this, because I've spent too much hours now to fix this and I'm getting crazy. It's really weird because it actually executes both animations... but one after the other instead of doing them in parallel like in the first execution.

*Actually the warning banner is a bit more complex than a simple TextView (LinearLayout) since I wanted a blink effect for "retrying..." text. But we shouldn't care about this.

Answer

It's pretty funny (for me) because I literally just answered a few minutes ago another question which is exactly the same problem as yours:

Please read my answer from here: Why does running a second viewpropertyanimation on a view break the animation listeners?

so from what I explained there, the fix is easy:

  • add .setStartDelay(0)
  • to crossFade() -> bannerNetworkConnected.animate()

so it resets the delay and they can run together.