appcompat-v7 v23.0.0 цвет строки состояния черный в ActionMode

ОБНОВЛЕНИЕ

Та же проблема присутствует в последнем приложении Gmail. Я до сих пор не понимаю, зачем Google вносить такие неприятные изменения в интерфейс. Одержимость во мне сходит с ума всякий раз, когда я это вижу

ВОПРОС

У меня есть эта странная проблема с appcompat-v7 23. Проблема, которую я собираюсь описать, не возникает с 22 сериями.

Вы можете получить исходный код, который воспроизводит эту форму выпуска: https://github.com/devserv/t/ После создания вы можете нажать и удерживать элемент в списке, чтобы активировать режим ActionMode

Проблема:

В ActionMode appcompat превращает строку состояния в черный цвет. Этого не произойдет, если я не использую следующие

<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>

в моем стиле v21, но я должен использовать его, потому что я хочу, чтобы мой навигационный ящик смотрел за строку состояния.


Раньше я использовал следующее, чтобы избежать черной строки состояния, когда ActionMode запускался и заканчивался

 public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.appColorPrimaryDark));
    }

}

 public void onDestroyActionMode(ActionMode actionMode) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        getActivity().getWindow().setStatusBarColor(getResources().getColor(android.R.color.transparent));
     }

    mMode = null;
}

Приведенный выше код не создавал / избегал черной строки состояния, но не работает должным образом в v23 appcompat. Вместо этого вы видите короткую черную строку состояния, когда ActionMode уничтожен. Похоже, это связано с анимацией, которая воспроизводится при уничтожении ActionMode.

Я пытался открыть отчеты об ошибках, но они были отклонены с комментариями.

Don't re-create bugs.

Я что-то пропустил?

Вот скриншоты для нормального и режима действия.

введите здесь описание изображения введите здесь описание изображения


person nLL    schedule 31.08.2015    source источник
comment
Вы пробовали с выпуском 23.0.1? Его развернули сегодня. Какой уровень API у вашего устройства?   -  person Mimmo Grottoli    schedule 04.09.2015
comment
Да, все так же. Они даже не принимают отчет об ошибке   -  person nLL    schedule 04.09.2015
comment
Эта проблема была исправлена ​​с помощью Это было исправлено с помощью com.android.support:design:28.0.0-rc01.   -  person Soren Stoutner    schedule 16.08.2018


Ответы (3)


Если проблема только в цвете, вы можете изменить его. Только для фиксированного цветового ресурса.

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

Очевидный ?colorPrimaryDark не будет работать, даже на API 21.


Представление, отвечающее за черный фон строки состояния, хранится в AppCompatDelegateImplV7.mStatusGuard. Вы можете получить делегата, вызвав getDelegate() из своей деятельности и получить доступ к полю mStatusGuard путем отражения. После запуска режима действий вы можете получить ссылку на это представление и настроить его по своему усмотрению.

Это было обнаружено в AppCompat 24.1.1.

person Eugen Pechanec    schedule 01.08.2016
comment
В androidx.appcompat 1.2.0 имя соответствующего цвета — abc_decor_view_status_guard. - person aax; 13.08.2020

В версии 23.0.0 v7 appcompat library появилась анимация, которая постепенно появляется и исчезает в режиме действия, когда он начинается и заканчивается, как вы можете прочитать здесь:

Режим действия исчез и работает, как задумано.

Изменения внесены в метод onDestroyActionMode в 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;
}

В версии 23.0.0 он был изменен на:

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;
}

Как видите, mWrapped.onDestroyActionMode(mode); вызывается сразу, а не по окончании анимации. Это то, что вызывает черную строку состояния в вашем приложении и в других приложениях, таких как Gmail и Keep.

Обходной путь, который вы нашли, работает, но, к сожалению, ненадежен, потому что, если анимация длится дольше, вы все равно можете увидеть черную строку состояния.

Я думаю, что Google должен исправить проблему и вызывать onDestroyActionMode только тогда, когда анимация действительно закончилась. Тем временем вы можете изменить это поведение, немного поразмыслив. Необходимо переопределить onSupportActionModeStarted в своей активности и вызвать метод 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();
    }
}
person Mattia Maestrini    schedule 06.09.2015
comment
Я был уверен, что это вызвано анимацией (см. мои ответы ниже), ваш ответ подтверждает это. Чего я не понимаю, так это того, что они настаивают на том, чтобы он работал по назначению. Это явно не так. - person nLL; 06.09.2015
comment
Я думаю, что они не совсем поняли проблему. Проблема не в самой анимации (очевидно, что она работает как надо), а в том, что onDestroyActionMode вызывается слишком рано. - person Mattia Maestrini; 06.09.2015
comment
Надеюсь, @chris-bane знает об этом. - person nLL; 06.09.2015
comment
Думал не буду реализовывать. Я принял ваш ответ, поскольку он ясно объясняет, что вызывает это. Спасибо за ваше время и усилия - person nLL; 06.09.2015

Никто? Вот обходной путь, который я придумал. Задерживать.

@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;

    }
person nLL    schedule 01.09.2015