Подтвердить что ты не робот

Android 4.4 - Прозрачные статусные/навигационные полосы - fitsSystemWindows/clipToPadding не работают через операции фрагмента

При использовании полупрозрачного статуса и навигационных панелей из новых Android 4.4 KitKat API, установка fitsSystemWindows="true" и clipToPadding="false" на ListView работает изначально. fitsSystemWindows="true" сохраняет список под панелью действий и над навигационной панелью, clipToPadding="false" позволяет прокручивать список под прозрачной навигационной панелью и делает последний элемент в списке прокручивается достаточно далеко, чтобы пройти панель навигации.

Однако при замене содержимого другим Fragment на FragmentTransaction эффект fitsSystemWindows исчезает, а фрагмент переходит под панель действий и панель навигации.

У меня есть кодовая база демо-исходного кода вместе с загружаемым APK в качестве примера: https://github.com/afollestad/kitkat-transparency-demo. Чтобы узнать, о чем я говорю, откройте демонстрационное приложение с устройства, использующего KitKat, коснитесь элемента в списке (который откроет другое действие) и коснитесь элемента в новом открывшемся действии. Фрагмент, который заменяет содержимое, находится под панелью действий, и clipToPadding работает некорректно (панель навигации закрывает последний элемент в списке, когда вы прокручиваете весь путь вниз).

Любые идеи? Любые разъяснения нужны? Я разместил до и после скриншотов моего личного приложения, которое было разработано для моего работодателя.

OneTwo

4b9b3361

Ответ 1

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

Класс SystemBarConfig SystemBarTint (см. здесь https://github.com/jgilfelt/SystemBarTint#systembarconfig) позволяет вводить вставки, которые я установил в качестве дополнения к списку в каждом фрагменте, а также использование clipToPadding="false" в списке.

У меня есть детали того, что я сделал на этом посту: http://mindofaandroiddev.wordpress.com/2013/12/28/making-the-status-bar-and-navigation-bar-transparent-with-a-listview-on-android-4-4-kitkat/

Ответ 2

Я боролся с той же проблемой вчера. Много подумав, я нашел элегантное решение этой проблемы.

Сначала я увидел метод requestFitSystemWindows() на ViewParent, и я попытался вызвать его в фрагменте onActivityCreated() (после Фрагмент привязан к иерархии представлений), но, к сожалению, это не повлияло. Я хотел бы увидеть конкретный пример того, как использовать этот метод.

Затем я нашел аккуратное обходное решение: я создал пользовательский FitsSystemWindowsFrameLayout, который я использую как контейнер в моих макетах, в качестве замены для классического FrameLayout. То, что он делает, это запоминание вставки окна, когда система fitSystemWindows() вызывается системой, затем она снова передает вызов его дочернему макету (макет фрагмента) сразу после добавления/присоединения фрагмента.

Здесь полный код:

public class FitsSystemWindowsFrameLayout extends FrameLayout {

    private Rect windowInsets = new Rect();
    private Rect tempInsets = new Rect();

    public FitsSystemWindowsFrameLayout(Context context) {
        super(context);
    }

    public FitsSystemWindowsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FitsSystemWindowsFrameLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
        windowInsets.set(insets);
        super.fitSystemWindows(insets);
        return false;
    }

    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        super.addView(child, index, params);
        tempInsets.set(windowInsets);
        super.fitSystemWindows(tempInsets);
    }
}

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

Ответ 3

Хорошо, так что это невероятно странно. Недавно я столкнулся с этой же проблемой, кроме моей - мягкая клавиатура. Сначала он работает, но если я добавляю транзакцию фрагмента, android:fitsSystemWindows="true" больше не работает. Я пробовал все решения здесь, никто из них не работал у меня.

Вот моя проблема: enter image description here

Вместо того, чтобы изменять размер моего представления, он подталкивает мое мнение, и это проблема.

Однако мне повезло и случайно наткнулся на ответ, который сработал у меня!

Итак, вот оно:

Прежде всего, моя тема приложения: Theme.AppCompat.Light.NoActionBar(если это актуально, может быть, это, android странно).

Маурици указал здесь что-то очень интересное, поэтому я хотел проверить, что он сказал, правда или нет. То, что он сказал, было правдой и в моем случае... ЕСЛИ вы не добавите этот атрибут в свою активность в манифесте Android вашего приложения:

enter image description here

После добавления:

android:windowSoftInputMode="adjustResize" 

для вашей активности, android:fitsSystemWindows="true" больше не игнорируется после транзакции фрагмента!

Однако, я предпочитаю, чтобы вы вызывали android:fitsSystemWindows="true" NOT в корневом макете вашего фрагмента. Одно из самых больших мест, где эта проблема будет возникать, - это если у вас есть EditText или ListView. Если вы застряли в этом затруднительном положении, как я, установите android:fitsSystemWindows="true" в дочернем корневом макете, как это:

enter image description here

ДА, это решение работает на всех устройствах Lollipop и pre-lollipop.

И вот доказательство: enter image description here

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

Спасибо всем!

Ответ 4

А голова для некоторых людей сталкивается с этой проблемой.

Ключ информации с методом fitSystemWindows, который выполняет большую часть работы:

Эта функция обходит иерархию по глубине. Такой же объект вставки содержимого распространяется по иерархии, поэтому любые изменения сделанные к нему, будут видны всеми следующими взглядами (включая потенциально выше в иерархии, так как это обход глубины). Первое представление, которое возвращает true, прервет весь обход.

Итак, если у вас есть другие фрагменты с представлениями контента, для которых fitsSystemWindows установлен в true, флаг будет потенциально проигнорирован. Я бы подумал о том, чтобы сделать ваш контейнер-фрагмент содержащим флаг fitsSystemWindows, если это возможно. В противном случае вручную добавьте дополнение.

Ответ 5

Я тоже очень сильно борюсь с этим. Я видел все ответы здесь. К сожалению, никто из них не исправлял мою проблему в 100% случаев. SystemBarConfig не работает всегда, так как он не может обнаружить панель на некоторых устройствах. Я посмотрел исходный код и нашел, где вставки хранятся внутри окна.

        Rect insets = new Rect();
        Window window = getActivity().getWindow();
        try {
            Class clazz = Class.forName("com.android.internal.policy.impl.PhoneWindow");
            Field field = clazz.getDeclaredField("mDecor");
            field.setAccessible(true);
            Object decorView = field.get(window);
            Field insetsField = decorView.getClass().getDeclaredField("mFrameOffsets");
            insetsField.setAccessible(true);
            insets = (Rect) insetsField.get(decorView);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

Вот как их получить. По-видимому, в Android L будет хороший способ получить эти вставки, но в то же время это может быть хорошим решением.

Ответ 6

Я столкнулся с той же проблемой. Когда я заменяю фрагмент. "FitsSystemWindows" не работает.

Я исправил код, добавив к вашему фрагменту

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    AndroidUtil.runOnUIThread(new Runnable() {
        @Override
        public void run() {
            ((ViewGroup) getView().getParent()).setFitsSystemWindows(true);
        }
    });
}

Ответ 7

В сочетании с ответом @BladeCoder я создал класс FittedFrameLayout, который выполняет две функции:

  • он не добавляет дополнение для себя
  • он просматривает все представления внутри своего контейнера и добавляет для них дополнение, но останавливается на самом нижнем слое (если обнаружен флаг fitssystemwindows, он не будет сканировать ребенка глубже, но все же на той же глубине или ниже).

    public class FittedFrameLayout extends FrameLayout {
        private Rect insets = new Rect();
    
        public FittedFrameLayout(Context context) {
            super(context);
        }
    
        public FittedFrameLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }
    
        public FittedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public FittedFrameLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            super(context, attrs, defStyleAttr, defStyleRes);
        }
    
        protected void setChildPadding(View view, Rect insets){
            if(!(view instanceof ViewGroup))
                return;
    
            ViewGroup parent = (ViewGroup) view;
            if (parent instanceof FittedFrameLayout)
                ((FittedFrameLayout)parent).fitSystemWindows(insets);
            else{
                if( ViewCompat.getFitsSystemWindows(parent))
                    parent.setPadding(insets.left,insets.top,insets.right,insets.bottom);
                else{
                    for (int i = 0, z = parent.getChildCount(); i < z; i++)
                        setChildPadding(parent.getChildAt(i), insets);
                }
            }
        }
    
        @Override
        protected boolean fitSystemWindows(Rect insets) {
            this.insets = insets;
            for (int i = 0, z = getChildCount(); i < z; i++)
                setChildPadding(getChildAt(i), insets);
    
            return true;
        }
    
        @Override
        public void addView(View child, int index, ViewGroup.LayoutParams params) {
            super.addView(child, index, params);
            setChildPadding(child, insets);
        }
    }
    

Ответ 8

Я разрешаю этот вопрос в 4.4

if(test){
    Log.d(TAG, "fit true ");
    relativeLayout.setFitsSystemWindows(true);
    relativeLayout.requestFitSystemWindows();
    getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}else {
    Log.d(TAG, "fit false");
    relativeLayout.setFitsSystemWindows(false);
    relativeLayout.requestFitSystemWindows();
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}