使用AnimationUtils显示和隐藏FAB时的竞争条件

Ale*_*ber 6 animation android android-animation animationutils floating-action-button

在允许用户通过社交网络登录的Android应用中,我FAB使用以下代码显示和隐藏a :

应用截图

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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

当我调用上述showFab方法足够慢时,这很有效.

在开始任何动画之前,我检查当前的FloatingActionButton可见性,以便动画只播放一次 - 即使我showFab(true)连续多次调用.

我的问题:

LoginFragment我的应用程序中显示a时,我首先发送请求以ServiceIntent从SQLite获取用户数据并调用以下方法将我的UI设置为"等待"状态:

private void setBusy(boolean busy) {
    mProgressBar.setVisibility(busy ? View.VISIBLE : View.INVISIBLE);
    showFab(!busy);
}
Run Code Online (Sandbox Code Playgroud)

几乎立即得到SQLite的回复 - 通过a LocalBroadcastManager和我再次调用上面的方法:setBusy(false).

然后发生错误并且FAB不可见.

如果我用无FAB动画代码替换方法一切正常:

private void showFab(boolean show) {
    mFab.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
}
Run Code Online (Sandbox Code Playgroud)

但是通过动画 - 似乎会出现赛车状况.

作为一种解决方法,我尝试取消两种动画 - 但这没有帮助:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

请建议在这里可以做些什么.

我已经多次在调试器中完成了我的应用程序.在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);
    }
}
Run Code Online (Sandbox Code Playgroud)

dav*_*nry 3

每个动画都在单独的线程中运行。正如你所说,这会导致竞争条件。

发生的事情是这样的:showFab 被触发为 true,然后为 false。

  • mShowFab onAnimationStart 方法执行并将 FAB 设置为可见。
  • mHideFab onAnimationStart 方法执行但不执行任何操作。
  • mShowFab onAnimationEnd 方法执行但不执行任何操作。
  • mHideFab onAnimationEnd 方法执行并将 FAB 设置为不可见。
  • 最终你会得到一个本来应该可见的不可见的 FAB。

第二次运行中的变量显示 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);
    }
}
Run Code Online (Sandbox Code Playgroud)