In an Android app allowing user logins via social networks I show and hide a FAB
using the following code:
public abstract class LoginFragment extends Fragment {
private FloatingActionButton mFab;
private Animation mShowFab;
private Animation mHideFab;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mShowFab = AnimationUtils.makeInAnimation(getContext(), false);
mShowFab.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
mFab.setVisibility(View.VISIBLE);
}
@Override
public void onAnimationEnd(Animation animation) {
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
mHideFab = AnimationUtils.makeOutAnimation(getContext(), true);
mHideFab.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mFab.setVisibility(View.INVISIBLE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
}
private void showFab(boolean show) {
boolean visible = mFab.isShown();
if (show && !visible) {
mFab.startAnimation(mShowFab);
} else if (!show && visible) {
mFab.startAnimation(mHideFab);
}
}
This works well, when I call the above showFab
method slow enough.
Before starting any animation I check for current FloatingActionButton
visibility, so that the animation is played only once - even if I call for example showFab(true)
several times in a row.
My problem:
When a LoginFragment
is shown in my app, I first send a request to a ServiceIntent
to fetch user data from SQLite and call the following method to set my UI to a "waiting" state:
private void setBusy(boolean busy) {
mProgressBar.setVisibility(busy ? View.VISIBLE : View.INVISIBLE);
showFab(!busy);
}
Almost immediately a response from SQLite comes back - via a LocalBroadcastManager
and I call the above method again: setBusy(false)
.
And then the error occurs and the FAB
is not visible.
If I replace the FAB
method by animation-less code everything works fine:
private void showFab(boolean show) {
mFab.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
But with animation - a racing condition seems to occur.
As a workaround I have tried canceling both animations - but this does not help:
private void showFab(boolean show) {
mShowFab.cancel();
mShowFab.reset();
mHideFab.cancel();
mHideFab.reset();
boolean visible = mFab.isShown();
if (show && !visible) {
mFab.startAnimation(mShowFab);
} else if (!show && visible) {
mFab.startAnimation(mHideFab);
}
}
Please suggest what could be done here.
I have stepped through my app in debugger numerous times already. The setBusy
(and showFab
) are called only twice when the Fragment is shown, but both calls happen very quickly - and the FAB
is not shown -
First run:
Second run:
UPDATE:
Unfortunately, making the method synchronized
does not help either - the FAB
stays hidden:
private synchronized void showFab(boolean show) {
mShowFab.cancel();
mShowFab.reset();
mHideFab.cancel();
mHideFab.reset();
boolean visible = mFab.isShown();
if (show && !visible) {
mFab.startAnimation(mShowFab);
} else if (!show && visible) {
mFab.startAnimation(mHideFab);
}
}
Each of your animations run in separate threads. This leads to a race condition as you said.
Here's whats going on: showFab is fired with true, then false.
The variables in your second run show that the mShowFab animation will never run.
You should be able to resolve the race condition by listening for the end of the hide animation. Something like this:
private void showFab(boolean show) {
if (show) {
// if you have an animation currently running and you want to show the fab
if (mFab.getAnimation() != null && !mFab.getAnimation().hasEnded()) {
// then wait for it to complete and begin the next one
mFab.getAnimation().setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
mFab.setVisibility(View.INVISIBLE);
mFab.startAnimation(mShowFab);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
} else {
mFab.startAnimation(mShowFab);
}
} else {
mFab.startAnimation(mHideFab);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With