Полупрозрачный навигационный ящик над строкой состояния не работает

86

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

Моя панель навигации находится над строкой состояния, но не имеет прозрачности. Я следил за кодом из этого сообщения SO, как было предложено в блоге разработчиков Google, ссылка выше Как мне использовать DrawerLayout для отображения поверх ActionBar / Toolbar и под строкой состояния? .

Ниже мой макет XML

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/my_drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <android.support.v7.widget.Toolbar
            android:id="@+id/my_awesome_toolbar"
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            android:minHeight="?attr/actionBarSize"
            android:background="@color/appPrimaryColour" />
    </LinearLayout>
    <LinearLayout android:id="@+id/linearLayout"
        android:layout_width="304dp"
        android:layout_height="match_parent"
        android:layout_gravity="left|start"
        android:fitsSystemWindows="true"
        android:background="#ffffff">
        <ListView android:id="@+id/left_drawer"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"></ListView>
    </LinearLayout>
</android.support.v4.widget.DrawerLayout>

Ниже представлена ​​тема моих приложений

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/appPrimaryColour</item>
        <item name="colorPrimaryDark">@color/appPrimaryColourDark</item>
        <item name="colorAccent">@color/appPrimaryColour</item>
        <item name="windowActionBar">false</item>
        <item name="windowActionModeOverlay">true</item>

    </style>

Ниже представлена ​​тема моих приложений v21

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/appPrimaryColour</item>
    <item name="colorPrimaryDark">@color/appPrimaryColourDark</item>
    <item name="colorAccent">@color/appPrimaryColour</item>
    <item name="windowActionBar">false</item>
    <item name="windowActionModeOverlay">true</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
</style>

Ниже мой метод onCreate

protected void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = (Toolbar) findViewById(R.id.my_awesome_toolbar);
    setSupportActionBar(toolbar);

    mDrawerLayout = (DrawerLayout)findViewById(R.id.my_drawer_layout);
    mDrawerList = (ListView)findViewById(R.id.left_drawer);

    mDrawerLayout.setStatusBarBackgroundColor(
        getResources().getColor(R.color.appPrimaryColourDark));

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
    {
        LinearLayout linearLayout = 
            (LinearLayout)findViewById(R.id.linearLayout);
        linearLayout.setElevation(30);
    }

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

Снимок экрана с непрозрачным изображением поверх строки состояния

Boardy
источник
Разместите снимок экрана с результатами.
Алок Наир
@Boardy тебе удалось заставить его работать?
Michele La Ferla
Невозможно установить эффект прозрачности в строке состояния, пожалуйста, избавьтесь от него.
Ронак Гадия

Ответы:

103

Фон строки состояния белый, это фон вашего ящика LinearLayout. Зачем? Вы - настройки fitsSystemWindows="true"для себя DrawerLayoutи LinearLayoutвнутри него. Это заставляет вас LinearLayoutрасширяться за строкой состояния (которая прозрачна). Таким образом, делаем фон для выдвижной части строки состояния белым.

введите описание изображения здесь
Если вы не хотите, чтобы ваш ящик выступал за строку состояния (хотите, чтобы фон для всей строки состояния был полупрозрачным), вы можете сделать две вещи:

1) Вы можете просто удалить любое значение фона из вашего LinearLayoutи раскрасить фон вашего контента внутри него. Или

2) Вы можете удалить fitsSystemWindows="true"из своего LinearLayout. Я считаю, что это более логичный и понятный подход. Вы также избежите отбрасывания тени под строкой состояния, где панель навигации не расширяется.

введите описание изображения здесь
Если вы хотите, чтобы ваш ящик выступал за строку состояния и имел полупрозрачный оверлей размером с строку состояния, вы можете использовать a ScrimInsetFrameLayoutв качестве контейнера для содержимого вашего ящика ( ListView) и установить фон строки состояния с помощью app:insetForeground="#4000". Конечно, вы можете измениться #4000на все, что захотите. Не забывай оставаться fitsSystemWindows="true"здесь!

Или, если вы не хотите накладывать свой контент и отображать только сплошной цвет, вы можете просто установить для своего фона LinearLayoutвсе, что захотите. Не забудьте установить фон вашего контента отдельно!

РЕДАКТИРОВАТЬ: вам больше не нужно заниматься этим. Пожалуйста, обратитесь к Библиотеке поддержки дизайна, чтобы упростить реализацию навигационного ящика / представления.

xip
источник
Спасибо за вашу помощь. Извините за задержку с возвращением, был безумно занят. Я назначил еще одну награду, чтобы вознаградить ваш ответ, как я изначально установил, но он закончился до того, как у меня было время реализовать ваши предложения. К сожалению, мне нужно подождать 23 часа, поэтому я добавлю его, как только смогу
Boardy
Осторожно! С вариантами 1 или 2 обязательно следите за своими перерисовками (вы можете проверить их в опциях разработчика в настройках телефона). Когда я устанавливал только фон содержимого внутри, все за ящиком тоже рисовалось. В остальном отличный ответ, определенно сэкономил мне время :)
Дайвик Даарун
1
Не могли бы вы объяснить, какое решение нам нужно использовать, используя библиотеку поддержки дизайна? У меня есть некоторые проблемы, даже с ответом, который опубликовал Крис Бэйнс.
mDroidd 05
29

Все, что вам нужно сделать, это использовать какой-то полупрозрачный цвет для строки состояния. Добавьте эти строки в свою тему v21:

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

Не забывайте, что color(ресурс) всегда должен быть в формате #AARRGGBB. Это означает, что цвет также будет включать альфа-значение.

Мрокзис
источник
Вы мужчина, но ... Вы можете сказать мне, почему это работает только в Activity, у которой есть Drawer? Остальные, те, у кого нет, они показывают серую строку состояния.
Хоакин Юрчук
15

Почему бы не использовать что-то подобное?

<android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

    <FrameLayout
            android:id="@+id/content_frame"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>

    <ListView
            android:id="@+id/left_drawer"
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            android:choiceMode="singleChoice"
            android:divider="@android:color/transparent"
            android:dividerHeight="0dp"
            android:background="#80111111"/>
</android.support.v4.widget.DrawerLayout>

В приведенном выше коде используется альфа-ресурс в, android:backgroundчтобы иметь возможность отображать прозрачность на панели действий.

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

Мишель Ла Ферла
источник
4
Почему бы и нет, но непонятно, что вы меняете и как это отвечает на вопрос.
rds
установил разделитель на прозрачный и использовал альфа-коды в цвете фона
Мишель Ла Ферла
9

Все упомянутые здесь ответы слишком старые и длинные. Лучшее и краткое решение, которое работает с последней версией Navigationview, -

@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
    super.onDrawerSlide(drawerView, slideOffset);

    try {
        //int currentapiVersion = android.os.Build.VERSION.SDK_INT;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
            // Do something for lollipop and above versions

        Window window = getWindow();

        // clear FLAG_TRANSLUCENT_STATUS flag:
        window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

        // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

        // finally change the color to any color with transparency

         window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDarktrans));}

    } catch (Exception e) {

        Crashlytics.logException(e);

    }
}

это изменит цвет вашей строки состояния на прозрачный, когда вы откроете ящик

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

@Override
public void onDrawerClosed(View drawerView) {
   super.onDrawerClosed(drawerView);
    try {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            // Do something for lollipop and above versions

            Window window = getWindow();

            // clear FLAG_TRANSLUCENT_STATUS flag:
            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

            // add FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS flag to the window
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

            // finally change the color again to dark
            window.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
        }
    } catch (Exception e) {
        Crashlytics.logException(e);
    }
}

а затем в основном макете добавьте одну строку, т.е.

            android:fitsSystemWindows="true"

и ваш макет ящика будет выглядеть как

            <android.support.v4.widget.DrawerLayout     
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:app="http://schemas.android.com/apk/res-auto"
            xmlns:tools="http://schemas.android.com/tools"
            android:id="@+id/drawer_layout"
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

и ваш вид навигации будет выглядеть как

    <android.support.design.widget.NavigationView
        android:id="@+id/navigation_view"
        android:layout_height="match_parent"
        android:layout_width="wrap_content"
        android:layout_gravity="start"
        android:fitsSystemWindows="true"
        app:headerLayout="@layout/navigation_header"
        app:menu="@menu/drawer"
        />

Я протестировал его, и он полностью работает. Надеюсь, он кому-то поможет. Возможно, это не лучший подход, но он работает плавно и прост в реализации. Отметьте это, если это поможет. Удачного кодирования :)

Гарри Шарма
источник
Благодарность! Это сэкономило мне много времени. Он отлично работает с последней версией NavigationView. Для тех, кто не может заставить это работать, убедитесь, что вы выбрали цвет с некоторой прозрачностью для «colorPrimaryDarktrans», иначе вы вообще не увидите никаких изменений.
Руи
Работает отлично, единственный недостаток в том, что строка состояния «мигает» при вызове onDrawerClosed, но если установить прозрачный цвет colorPrimaryDarktrans на # 05000000, это почти незаметно
Кирилл Кармазин
8

Вам нужно обернуть макет панели навигации в файл ScrimLayout.

A в ScrimLayoutосновном рисует полупрозрачный прямоугольник над макетом панели навигации. Чтобы получить размер вставки, просто переопределите fitSystemWindowsв вашем ScrimLayout.

@Override
protected boolean fitSystemWindows(Rect insets) {
    mInsets = new Rect(insets);
    return true;
}

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

Пример реализации можно найти в источнике Google IO App. Здесь вы можете увидеть, как он используется в макете xml.

Маркус привет
источник
5

Если вы хотите, чтобы панель навигации находилась над строкой состояния и была полупрозрачной над строкой состояния. Google I / O Android App Source предоставляет хорошее решение ( APK в Play Store не обновляются до последней версии)

Сначала вам понадобится ScrimInsetFrameLayout

/*
* Copyright 2014 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* A layout that draws something in the insets passed to {@link #fitSystemWindows(Rect)}, i.e. the area above UI chrome
* (status and navigation bars, overlay action bars).
*/
public class ScrimInsetsFrameLayout extends FrameLayout {
    private Drawable mInsetForeground;

    private Rect mInsets;
    private Rect mTempRect = new Rect();
    private OnInsetsCallback mOnInsetsCallback;

    public ScrimInsetsFrameLayout(Context context) {
        super(context);
        init(context, null, 0);
    }

    public ScrimInsetsFrameLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

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

    private void init(Context context, AttributeSet attrs, int defStyle) {
        final TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.ScrimInsetsView, defStyle, 0);
        if (a == null) {
            return;
        }
        mInsetForeground = a.getDrawable(R.styleable.ScrimInsetsView_insetForeground);
        a.recycle();

        setWillNotDraw(true);
    }

    @Override
    protected boolean fitSystemWindows(Rect insets) {
        mInsets = new Rect(insets);
        setWillNotDraw(mInsetForeground == null);
        ViewCompat.postInvalidateOnAnimation(this);
        if (mOnInsetsCallback != null) {
            mOnInsetsCallback.onInsetsChanged(insets);
        }
        return true; // consume insets
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        int width = getWidth();
        int height = getHeight();
        if (mInsets != null && mInsetForeground != null) {
            int sc = canvas.save();
            canvas.translate(getScrollX(), getScrollY());

            // Top
            mTempRect.set(0, 0, width, mInsets.top);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Bottom
            mTempRect.set(0, height - mInsets.bottom, width, height);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Left
            mTempRect.set(0, mInsets.top, mInsets.left, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            // Right
            mTempRect.set(width - mInsets.right, mInsets.top, width, height - mInsets.bottom);
            mInsetForeground.setBounds(mTempRect);
            mInsetForeground.draw(canvas);

            canvas.restoreToCount(sc);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(this);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mInsetForeground != null) {
            mInsetForeground.setCallback(null);
        }
    }

    /**
     * Allows the calling container to specify a callback for custom processing when insets change (i.e. when
     * {@link #fitSystemWindows(Rect)} is called. This is useful for setting padding on UI elements based on
     * UI chrome insets (e.g. a Google Map or a ListView). When using with ListView or GridView, remember to set
     * clipToPadding to false.
     */
    public void setOnInsetsCallback(OnInsetsCallback onInsetsCallback) {
        mOnInsetsCallback = onInsetsCallback;
    }

    public static interface OnInsetsCallback {
        public void onInsetsChanged(Rect insets);
    }
}

Затем в вашем XML-макете измените эту часть

<LinearLayout android:id="@+id/linearLayout"
    android:layout_width="304dp"
    android:layout_height="match_parent"
    android:layout_gravity="left|start"
    android:fitsSystemWindows="true"
    android:background="#ffffff">
    <ListView android:id="@+id/left_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:choiceMode="singleChoice"></ListView>
</LinearLayout>

Измените LinearLayout на свой ScrimInsetFrameLayout следующим образом

<com.boardy.util.ScrimInsetFrameLayout
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/linearLayout"
    android:layout_width="304dp"
    android:layout_height="match_parent"
    android:layout_gravity="left|start"
    android:fitsSystemWindows="true"
    app:insetForeground="#4000">
    <ListView android:id="@+id/left_drawer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:choiceMode="singleChoice"></ListView>
</com.boardy.util.ScrimInsetFrameLayout>
Skyfishjy
источник
Привет, спасибо за ответ .. Не могу найти то, откуда мы получаем OnInsetsCallback ... Заранее спасибо.
Ашокчакраварти Нагараджан,
можешь поставить ScrimInsetsView_insetForegroundатрибут.
reegan29,
1
Использование android.support.design.internal.ScrimInsetsFrameLayoutиз библиотеки поддержки
Роман,
4

Мне нужно было реализовать аналогичную функцию в моем проекте. И чтобы сделать мой ящик прозрачным, я просто использую прозрачность для шестнадцатеричных цветов . Используя 6 шестнадцатеричных цифр, у вас есть 2 шестнадцатеричные цифры для каждого значения красного, зеленого и синего соответственно. но если вы поместите две дополнительные цифры (8 шестнадцатеричных цифр), у вас будет ARGB (две цифры для альфа-значения) ( смотрите здесь ).

Вот значения непрозрачности в шестнадцатеричном формате: для 2 дополнительных цифр

100%  FF
95%  F2
90%  E6
85%  D9
80%  CC
75%  BF
70%  B3
65%  A6
60%  99
55%  8C
50%  80
45%  73
40%  66
35%  59
30%  4D
25%  40
20%  33
15%  26
10%  1A
5%  0D
0%  00

Например: если у вас есть шестнадцатеричный цвет (# 111111), просто установите одно из значений непрозрачности, чтобы получить прозрачность. вот так: (# 11111100)

Мой ящик не закрывает панель действий (как ваш), но прозрачность также может применяться в этих случаях. Вот мой код:

     <ListView
          android:id="@+id/left_drawer"
          android:layout_width="240dp"
          android:layout_height="match_parent"
          android:layout_gravity="start"
          android:background="#11111100"
          android:choiceMode="singleChoice"
          android:divider="@android:color/transparent"
          android:dividerHeight="0dp" />
</android.support.v4.widget.DrawerLayout>

А вот еще одна статья, которая поможет вам разобраться в альфа-кодах шестнадцатеричных цветов.

Fdsilva
источник
1

Существующие решения довольно устарели. Вот последнее решение, которое должно отлично работать с AndroidX или библиотеками материалов.

  • Добавьте android:fitsSystemWindows="true"в корневой вид вашего макета, который в вашем случае является DrawerLayout.

    Это указывает представлению использовать весь экран для измерения и макета, перекрывая строку состояния.

  • Добавьте в свой стиль XML следующее

      <item name="android:windowDrawsSystemBarBackgrounds">true</item>
      <item name="android:windowTranslucentStatus">true</item>
    

    windowTranslucentStatusсделает строку состояния прозрачной,
    windowDrawsSystemBarBackgroundsуказывает строке состояния рисовать что-либо под ней, вместо того, чтобы быть черной пустотой.

  • Позвоните DrawerLayout.setStatusBarBackground()или DrawerLayout.setStatusBarBackgroundColor()в своей основной деятельности

    Это сообщает DrawerLayout о необходимости раскрасить область строки состояния.

Браун Смит
источник
0

Выше были хорошие решения, но в моем случае они не сработали,

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

цель: изменить цвет строки состояния и панели навигации при открытии панели навигации и восстановить ее при закрытии.

решение :

        binding.drawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() 
        {
        @Override
        public void onDrawerSlide(@NonNull View drawerView, float slideOffset) {
        }

        @Override
        public void onDrawerOpened(@NonNull View drawerView) {
            getWindow().setStatusBarColor(getResources().getColor(R.color.forground));
            getWindow().setNavigationBarColor(getResources().getColor(R.color.forground));
        }

        @Override
        public void onDrawerClosed(@NonNull View drawerView) {
            getWindow().setStatusBarColor(getResources().getColor(R.color.yellow));
            getWindow().setNavigationBarColor(getResources().getColor(R.color.yellow));
        }

        @Override
        public void onDrawerStateChanged(int newState) {
        }
    });
Абхишек Гарг
источник
0

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

    <item name="android:windowTranslucentStatus">true</item>

это заставит его работать.

Кешав Бхаман
источник