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

Я работаю над проектом 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);
    }

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

Screenshot showing non transparent over the status bar

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

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

<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

Вам нужно обернуть свой макет ящика перемещения внутри ScrimLayout.

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

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

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

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

Ответ 5

Если вы хотите, чтобы панель навигации переходила через панель состояния и была полупрозрачной поверх строки состояния. Google App-источник для Android для ввода-вывода Google является хорошим решением (APK в Play Маркете не обновлены до последней версии)

Сначала вам понадобится 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>

Ответ 6

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

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

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>

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

Ответ 7

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

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

а затем в главном макете добавьте одну строку i.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"
        />

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