appcompat-v7 v23.0.0 statusbar color black when in ActionMode

后端 未结 3 1839
误落风尘
误落风尘 2020-12-15 01:46

UPDATE

Same issue present in the latest Gmail app. I still don\'t understand why would Google make such unpleasant UI change. Obsessive in me goes cr

相关标签:
3条回答
  • 2020-12-15 02:05

    In case only the color is the issue, you can change it. Only to a fixed color resource.

    <color name="abc_input_method_navigation_guard" tools:override="true">@color/primary_dark</color>
    

    Obvious ?colorPrimaryDark will not work, not even on API 21.


    The view responsible for the black status bar background is stored in AppCompatDelegateImplV7.mStatusGuard. You can get the delegate by calling getDelegate() from your activity and access mStatusGuard field by reflection. After starting the action mode you can get a reference to this view and customize it however you like.

    This was found in AppCompat 24.1.1.

    0 讨论(0)
  • 2020-12-15 02:10

    The version 23.0.0 of v7 appcompat library introduced an animation that fades in and out the action mode when it's started and finished as you can read here:

    The action mode has fades in and is working as intended.

    The changes are made in the method onDestroyActionMode in AppCompatDelegateImplV7:

    public void onDestroyActionMode(ActionMode mode) {
        mWrapped.onDestroyActionMode(mode);
        if (mActionModePopup != null) {
            mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
            mActionModePopup.dismiss();
        } else if (mActionModeView != null) {
            mActionModeView.setVisibility(View.GONE);
            if (mActionModeView.getParent() != null) {
                ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
            }
        }
        if (mActionModeView != null) {
            mActionModeView.removeAllViews();
        }
        if (mAppCompatCallback != null) {
            mAppCompatCallback.onSupportActionModeFinished(mActionMode);
        }
        mActionMode = null;
    }
    

    In version 23.0.0 it was changed to:

    public void onDestroyActionMode(ActionMode mode) {
        mWrapped.onDestroyActionMode(mode);
        if (mActionModePopup != null) {
            mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
        }
    
        if (mActionModeView != null) {
            endOnGoingFadeAnimation();
            mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0f);
            mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(View view) {
                    mActionModeView.setVisibility(View.GONE);
                    if (mActionModePopup != null) {
                        mActionModePopup.dismiss();
                    } else if (mActionModeView.getParent() instanceof View) {
                        ViewCompat.requestApplyInsets((View) mActionModeView.getParent());
                    }
                    mActionModeView.removeAllViews();
                    mFadeAnim.setListener(null);
                    mFadeAnim = null;
                }
            });
        }
        if (mAppCompatCallback != null) {
            mAppCompatCallback.onSupportActionModeFinished(mActionMode);
        }
        mActionMode = null;
    }
    

    As you can see mWrapped.onDestroyActionMode(mode); is called immediately, not when the animation ends. This is what cause the black status bar in your app and in other apps like Gmail and Keep.

    The workaround that you found works, but unfortunately is not reliable, because if the animation takes longer you could see the black status bar anyway.

    I think Google should correct the issue and call onDestroyActionMode only when the animation is really ended. In the mean time you can change this behaviour with a bit of reflections. It is necessary to override onSupportActionModeStarted in your activity and call the method fixActionModeCallback:

    @Override
    public void onSupportActionModeStarted(ActionMode mode) {
        super.onSupportActionModeStarted(mode);
    
        //Call this method
        fixActionModeCallback(this, mode);
    }
    
    private void fixActionModeCallback(AppCompatActivity activity, ActionMode mode) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
            return;
    
        if (!(mode instanceof StandaloneActionMode))
            return;
    
        try {
            final Field mCallbackField = mode.getClass().getDeclaredField("mCallback");
            mCallbackField.setAccessible(true);
            final Object mCallback = mCallbackField.get(mode);
    
            final Field mWrappedField = mCallback.getClass().getDeclaredField("mWrapped");
            mWrappedField.setAccessible(true);
            final ActionMode.Callback mWrapped = (ActionMode.Callback) mWrappedField.get(mCallback);
    
            final Field mDelegateField = AppCompatActivity.class.getDeclaredField("mDelegate");
            mDelegateField.setAccessible(true);
            final Object mDelegate = mDelegateField.get(activity);
    
            mCallbackField.set(mode, new ActionMode.Callback() {
    
                @Override
                public boolean onCreateActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
                    return mWrapped.onCreateActionMode(mode, menu);
                }
    
                @Override
                public boolean onPrepareActionMode(android.support.v7.view.ActionMode mode, Menu menu) {
                    return mWrapped.onPrepareActionMode(mode, menu);
                }
    
                @Override
                public boolean onActionItemClicked(android.support.v7.view.ActionMode mode, MenuItem item) {
                    return mWrapped.onActionItemClicked(mode, item);
                }
    
                @Override
                public void onDestroyActionMode(final android.support.v7.view.ActionMode mode) {
                    Class mDelegateClass = mDelegate.getClass().getSuperclass();
                    Window mWindow = null;
                    PopupWindow mActionModePopup = null;
                    Runnable mShowActionModePopup = null;
                    ActionBarContextView mActionModeView = null;
                    AppCompatCallback mAppCompatCallback = null;
                    ViewPropertyAnimatorCompat mFadeAnim = null;
                    android.support.v7.view.ActionMode mActionMode = null;
    
                    Field mFadeAnimField = null;
                    Field mActionModeField = null;
    
                    while (mDelegateClass != null) {
                        try {
                            if (TextUtils.equals("AppCompatDelegateImplV7", mDelegateClass.getSimpleName())) {
                                Field mActionModePopupField = mDelegateClass.getDeclaredField("mActionModePopup");
                                mActionModePopupField.setAccessible(true);
                                mActionModePopup = (PopupWindow) mActionModePopupField.get(mDelegate);
    
                                Field mShowActionModePopupField = mDelegateClass.getDeclaredField("mShowActionModePopup");
                                mShowActionModePopupField.setAccessible(true);
                                mShowActionModePopup = (Runnable) mShowActionModePopupField.get(mDelegate);
    
                                Field mActionModeViewField = mDelegateClass.getDeclaredField("mActionModeView");
                                mActionModeViewField.setAccessible(true);
                                mActionModeView = (ActionBarContextView) mActionModeViewField.get(mDelegate);
    
                                mFadeAnimField = mDelegateClass.getDeclaredField("mFadeAnim");
                                mFadeAnimField.setAccessible(true);
                                mFadeAnim = (ViewPropertyAnimatorCompat) mFadeAnimField.get(mDelegate);
    
                                mActionModeField = mDelegateClass.getDeclaredField("mActionMode");
                                mActionModeField.setAccessible(true);
                                mActionMode = (android.support.v7.view.ActionMode) mActionModeField.get(mDelegate);
    
                            } else if (TextUtils.equals("AppCompatDelegateImplBase", mDelegateClass.getSimpleName())) {
                                Field mAppCompatCallbackField = mDelegateClass.getDeclaredField("mAppCompatCallback");
                                mAppCompatCallbackField.setAccessible(true);
                                mAppCompatCallback = (AppCompatCallback) mAppCompatCallbackField.get(mDelegate);
    
                                Field mWindowField = mDelegateClass.getDeclaredField("mWindow");
                                mWindowField.setAccessible(true);
                                mWindow = (Window) mWindowField.get(mDelegate);
                            }
    
                            mDelegateClass = mDelegateClass.getSuperclass();
                        } catch (NoSuchFieldException e) {
                            e.printStackTrace();
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
    
                    if (mActionModePopup != null) {
                        mWindow.getDecorView().removeCallbacks(mShowActionModePopup);
                    }
    
                    if (mActionModeView != null) {
                        if (mFadeAnim != null) {
                            mFadeAnim.cancel();
                        }
    
                        mFadeAnim = ViewCompat.animate(mActionModeView).alpha(0.0F);
    
                        final PopupWindow mActionModePopupFinal = mActionModePopup;
                        final ActionBarContextView mActionModeViewFinal = mActionModeView;
                        final ViewPropertyAnimatorCompat mFadeAnimFinal = mFadeAnim;
                        final AppCompatCallback mAppCompatCallbackFinal = mAppCompatCallback;
                        final android.support.v7.view.ActionMode mActionModeFinal = mActionMode;
                        final Field mFadeAnimFieldFinal = mFadeAnimField;
                        final Field mActionModeFieldFinal = mActionModeField;
    
                        mFadeAnim.setListener(new ViewPropertyAnimatorListenerAdapter() {
                            public void onAnimationEnd(View view) {
                                mActionModeViewFinal.setVisibility(View.GONE);
                                if (mActionModePopupFinal != null) {
                                    mActionModePopupFinal.dismiss();
                                } else if (mActionModeViewFinal.getParent() instanceof View) {
                                    ViewCompat.requestApplyInsets((View) mActionModeViewFinal.getParent());
                                }
    
                                mActionModeViewFinal.removeAllViews();
                                mFadeAnimFinal.setListener((ViewPropertyAnimatorListener) null);
    
                                try {
                                    if (mFadeAnimFieldFinal != null) {
                                        mFadeAnimFieldFinal.set(mDelegate, null);
                                    }
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
    
                                mWrapped.onDestroyActionMode(mode);
    
                                if (mAppCompatCallbackFinal != null) {
                                    mAppCompatCallbackFinal.onSupportActionModeFinished(mActionModeFinal);
                                }
    
                                try {
                                    if (mActionModeFieldFinal != null) {
                                        mActionModeFieldFinal.set(mDelegate, null);
                                    }
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                }
                            }
                        });
                    }
                }
            });
    
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    
    0 讨论(0)
  • 2020-12-15 02:21

    Nobody? Here is the workaround I came up with. Delay.

    @Override
        public void onDestroyActionMode(ActionMode mode) {
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            getActivity().getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent));
                        }
                        catch(Exception e)
                        {
                            e.printStackTrace();
                        }
                    }
                }, 400);
    
            }
            mActionMode = null;
    
        }
    
    0 讨论(0)
提交回复
热议问题