在允许用户通过社交网络登录的Android应用中,我使用以下代码显示和隐藏FAB
:
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);
}
}
当我将上述showFab
方法调用得足够慢时,这很有效。
在开始任何动画之前,我会检查当前FloatingActionButton
的可见度,以便动画只播放一次 - 即使我连续多次调用showFab(true)
例子
我的问题:
当我的应用中显示LoginFragment
时,我首先向ServiceIntent
发送请求以从SQLite获取用户数据,并调用以下方法将我的UI设置为“等待”状态:
private void setBusy(boolean busy) {
mProgressBar.setVisibility(busy ? View.VISIBLE : View.INVISIBLE);
showFab(!busy);
}
几乎立即得到SQLite的回复 - 通过LocalBroadcastManager
我再次调用上述方法:setBusy(false)
。
然后发生错误,FAB
不可见。
如果我用无动画代码替换FAB
方法,一切正常:
private void showFab(boolean show) {
mFab.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
但是通过动画 - 似乎会出现赛车状况。
作为一种解决方法,我尝试取消这两种动画 - 但这没有帮助:
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);
}
}
请建议可以在这里做些什么。
我已经多次在调试器中完成了我的应用程序。显示片段时,setBusy
(和showFab
)仅被调用两次,但两次调用都很快发生 - 并且FAB
未显示 -
首次运行:
第二次运行:
更新
不幸的是,制作方法synchronized
也没有帮助 - FAB
保持隐藏状态:
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);
}
}
答案 0 :(得分:3)
每个动画都在不同的线程中运行。如你所说,这导致了竞争条件。
以下是最新消息: showFab被触发为true,然后是false。
第二次运行中的变量显示mShowFab动画永远不会运行。
您应该能够通过侦听隐藏动画的结束来解决竞争条件。像这样:
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);
}
}
答案 1 :(得分:1)
首先要指出的是,没有什么能比得上赛车条件。正确的术语只是竞争条件。记住未来的这一点; - )
我想你正在使用BroadcastReceiver来接收消息,并且你已经创建了对象并告诉它由于动画而在一个单独的线程中运行。这意味着您的showFab方法可以一次调用两次。要处理此问题,请将方法定义为synchronized。
答案 2 :(得分:0)
这是我自己的解决方案 -
首先,使用synchronized
在这里没有用,因为动画在同一个线程中运行。
我的解决方案是引入一个单独的boolean
变量来保存预期的最终FAB状态(可见或不可见):
private FloatingActionButton mFab;
private boolean mFabVisible;
private Animation mShowFab;
private Animation mHideFab;
private void showFab(boolean show) {
if (show && !mFabVisible) {
mFab.startAnimation(mShowFab);
} else if (!show && mFabVisible) {
mFab.startAnimation(mHideFab);
}
mFabVisible = show;
}
另外,我认为另一种可能性是两次调用mFab.setVisibility(View.VISIBLE)
- 如下面的代码所示。但我还没有真正测试过它:
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) {
mFab.setVisibility(View.VISIBLE);
}
@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) {
}
});