Szuflada nawigacji półprzezroczysta nad paskiem stanu nie działa

86

Pracuję nad projektem Android i wdrażam szufladę nawigacji. Czytam nową specyfikację Material Design i Listę kontrolną Material Design .
Specyfikacja mówi, że wysuwany panel powinien unosić się nad wszystkim innym, w tym paskiem stanu, i być półprzezroczysty nad paskiem stanu.

Mój panel nawigacyjny znajduje się nad paskiem stanu, ale nie ma żadnej przezroczystości. Postępowałem zgodnie z kodem z tego posta SO, zgodnie z sugestią na blogu Google Developers, łącze powyżej Jak używać DrawerLayout do wyświetlania nad ActionBar / Toolbar i pod paskiem stanu? .

Poniżej znajduje się mój układ 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>

Poniżej znajduje się motyw moich aplikacji

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

Poniżej znajduje się motyw moich aplikacji w wersji 21

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

Poniżej znajduje się moja metoda 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);
    }

Poniżej znajduje się zrzut ekranu mojej szuflady nawigacji, pokazujący, że górna część nie jest półprzezroczysta

Zrzut ekranu przedstawiający nieprzezroczysty nad paskiem stanu

Boardy
źródło
Opublikuj zrzut ekranu swoich wyników.
Alok Nair
@Boardy czy udało ci się zmusić to do pracy?
Michele La Ferla
Nie można ustawić efektu przezroczystości na pasku stanu. Pozbądź się go.
Ronak Gadhia,

Odpowiedzi:

103

Tło Twojego paska stanu jest białe, czyli tło Twojej szuflady LinearLayout. Czemu? Jesteś ustawieniem fitsSystemWindows="true"dla siebie DrawerLayouti tego, co jest w LinearLayoutśrodku. Powoduje LinearLayoutto rozszerzenie się za pasek stanu (który jest przezroczysty). W ten sposób tło dla szuflady na pasku stanu jest białe.

wprowadź opis obrazu tutaj
Jeśli nie chcesz, aby szuflada znajdowała się poza paskiem stanu (chcesz mieć półprzezroczyste tło dla całego paska stanu), możesz zrobić dwie rzeczy:

1) Możesz po prostu usunąć dowolną wartość tła ze swojego LinearLayouti pokolorować tło zawartości w nim zawartej. Lub

2) Możesz usunąć fitsSystemWindows="true"z LinearLayout. Myślę, że jest to bardziej logiczne i czystsze podejście. Unikniesz także rzucania cienia pod paskiem stanu, gdzie szuflada nawigacji się nie rozciąga.

wprowadź opis obrazu tutaj
Jeśli chcesz, aby szuflada ScrimInsetFrameLayoutznajdowała się poza paskiem stanu i miała półprzezroczystą nakładkę o rozmiarze paska stanu, możesz użyć jako kontenera zawartości szuflady ( ListView) i ustawić tło paska stanu za pomocą app:insetForeground="#4000". Oczywiście możesz zmienić #4000wszystko, co chcesz. Nie zapomnij fitsSystemWindows="true"tu zostać !

Lub jeśli nie chcesz nakładać treści i wyświetlać tylko jednolity kolor, możesz po prostu ustawić tło LinearLayoutna dowolne. Nie zapomnij jednak o oddzielnym ustawieniu tła treści!

EDYCJA: Nie musisz już się tym zajmować. Zapoznaj się z Biblioteką wsparcia projektowania, aby uzyskać nieco łatwiejszą implementację szuflady nawigacji / widoku.

xip
źródło
Dzięki za pomoc. Przepraszam za opóźnienie w powrocie, byłem szalenie zajęty. Ustawiłem kolejną nagrodę, aby nagrodzić twoją odpowiedź, tak jak pierwotnie ustawiłem, ale skończyło się, zanim zdążyłem wdrożyć twoje sugestie. Niestety muszę poczekać 23 godziny, więc dodam jak najszybciej
Boardy
Uwaga! W przypadku opcji 1 lub 2 koniecznie obejrzyj swoje overdraw (możesz je sprawdzić w opcjach deweloperskich w ustawieniach telefonu). Kiedy ustawiałem tylko tło treści wewnątrz, wszystko za szufladą również się rysowało. W przeciwnym razie świetna odpowiedź, zdecydowanie zaoszczędziła mi trochę czasu :)
Daiwik Daarun
1
Czy możesz wyjaśnić, jakiego rozwiązania potrzebujemy, korzystając z biblioteki wsparcia projektowania? Mam pewne problemy, nawet z odpowiedzią, którą opublikował Chris Banes.
mDroidd
29

Wszystko, co musisz zrobić, to użyć półprzezroczystego koloru paska stanu. Dodaj te wiersze do motywu w wersji 21:

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

Nie zapominaj, że color(zasób) musi zawsze mieć format #AARRGGBB. Oznacza to, że kolor będzie zawierał również wartość alfa.

mroczis
źródło
Jesteś mężczyzną, ale ... Czy możesz mi powiedzieć, dlaczego to działa tylko w Działaniu, które ma szufladę? Pozostali, ci, którzy tego nie zrobili, mają szary pasek stanu.
Joaquin Iurchuk
15

Dlaczego nie użyć czegoś podobnego do tego?

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

Powyższy kod używa zasobu alfa w, android:backgroundaby móc pokazać przezroczystość na pasku akcji.

Jak pokazują inne odpowiedzi, można to zrobić na inne sposoby za pomocą kodu. Moja odpowiedź powyżej, robi to, co niezbędne w pliku układu xml, który moim zdaniem można łatwo edytować.

Michele La Ferla
źródło
4
Dlaczego nie, ale nie jest jasne, co zmieniasz i jak to odpowiada na pytanie.
rds
ustaw dzielnik na przezroczyste i używane kody alfa w kolorze tła
Michele La Ferla
9

Wszystkie wymienione tutaj odpowiedzi są zbyt stare i zbyt obszerne. Najlepszym i krótkim rozwiązaniem, które współpracuje z najnowszym Navigationview jest

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

    }
}

spowoduje to zmianę koloru paska stanu na przezroczysty po otwarciu szuflady

Teraz, gdy zamkniesz szufladę, musisz ponownie zmienić kolor paska stanu na ciemny, więc możesz to zrobić w ten sposób.

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

a następnie w głównym układzie dodaj pojedynczą linię, tj

            android:fitsSystemWindows="true"

a układ twojej szuflady będzie wyglądał

            <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">

a widok nawigacji będzie wyglądał

    <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"
        />

Przetestowałem go i jest w pełni sprawny, mam nadzieję, że to komuś pomoże, może nie jest to najlepsze podejście, ale działa płynnie i jest łatwe do wdrożenia. Zaznacz, jeśli to pomoże. Miłego kodowania :)

Harry Sharma
źródło
Dzięki! To zaoszczędziło mi dużo czasu. Działa doskonale z najnowszym NavigationView. Dla każdego, kto nie może tego zrobić, upewnij się, że wybrałeś kolor z pewną przezroczystością dla „colorPrimaryDarktrans”, w przeciwnym razie nie zobaczysz żadnej zmiany.
Rui
Działa doskonale, jedyną wadą jest to, że pasek stanu `` miga '' po wywołaniu onDrawerClosed, ale jeśli ustawisz kolor przezroczysty PrimaryDarktrans na # 05000000, jest to prawie niezauważalne
Kirill Karmazin
8

Musisz owinąć układ szuflady nawigacji wewnątrz pliku ScrimLayout.

ScrimLayoutZasadzie rysuje półprzezroczystą prostokąt nad szufladą układu nawigacji. Aby pobrać rozmiar wstawki, po prostu zastąp ją fitSystemWindowsw pliku ScrimLayout.

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

Później zastąp, onDrawaby narysować półprzezroczysty prostokąt.

Przykładem wdrażania można znaleźć w źródle Google IO App. Tutaj możesz zobaczyć, jak jest używany w formacie XML układu.

Markus Cześć
źródło
5

Jeśli chcesz, aby panel nawigacyjny znajdował się nad paskiem stanu i był półprzezroczysty nad paskiem stanu. Google I / O Android App Source zapewnia dobre rozwiązanie ( APK w Play Store nie są aktualizowane do ostatniej wersji)

Najpierw potrzebujesz 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);
    }
}

Następnie w swoim układzie XML zmień tę część

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

Zmień LinearLayout na ScrimInsetFrameLayout w ten sposób

<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
źródło
Cześć, dzięki za odpowiedź ... Nie mogę znaleźć tego, skąd mamy OnInsetsCallback ... Z góry dziękuję.
Ashokchakravarthi Nagarajan
czy możesz umieścić ScrimInsetsView_insetForegroundatrybut.
reegan 29
1
Użyj android.support.design.internal.ScrimInsetsFrameLayoutz biblioteki wsparcia
Roman
4

Miałem podobną funkcję do zaimplementowania w swoim projekcie. Aby moja szuflada była przezroczysta, po prostu używam przezroczystości dla kolorów szesnastkowych . Używając 6 cyfr szesnastkowych, masz 2 cyfry szesnastkowe dla każdej wartości, odpowiednio, czerwonej, zielonej i niebieskiej. ale jeśli wstawisz dwie dodatkowe cyfry (8 cyfr szesnastkowych), masz ARGB (dwie cyfry dla wartości alfa) ( Spójrz tutaj ).

Oto wartości nieprzezroczystości szesnastkowej: dla 2 dodatkowych cyfr

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

Na przykład: jeśli masz kolor de hex (# 111111), po prostu umieść jedną z wartości krycia, aby uzyskać przezroczystość. w ten sposób: (# 11111100)

Moja szuflada nie zakrywa paska akcji (tak jak twoja), ale przezroczystość można zastosować również w tych przypadkach. Oto mój kod:

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

A oto kolejny artykuł, który pomoże ci zrozumieć kody alfa w kolorach szesnastkowych.

fdsilva
źródło
1

Istniejące rozwiązania są dość przestarzałe. Oto najnowsze rozwiązanie, które powinno idealnie działać na bibliotekach AndroidX lub Material.

  • Dodaj android:fitsSystemWindows="true"do katalogu głównego Widok układu, którym jest DrawerLayout dla twojego przypadku.

    To mówi Widokowi, aby używał całego ekranu do pomiaru i układu, nakładając się na pasek stanu.

  • Dodaj następujące elementy do swojego stylu XML

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

    windowTranslucentStatussprawi, że pasek stanu będzie przezroczysty,
    windowDrawsSystemBarBackgroundspowie mu, aby narysował cokolwiek pod nim, zamiast być czarną pustką.

  • Zadzwoń DrawerLayout.setStatusBarBackground()lub DrawerLayout.setStatusBarBackgroundColor()w swojej głównej aktywności

    To mówi DrawerLayout, aby pokolorował obszar paska stanu.

Brown Smith
źródło
0

Przede wszystkim dobre rozwiązania, ale nie sprawdziły się w moim przypadku,

mój przypadek: pasek stanu ma ustawiony żółty kolor już przy użyciu stylów, nie jest przezroczysty. a kolor mojej szuflady nawigacji jest biały, teraz za każdym razem, gdy rysuję szufladę nawigacji, zawsze znajduje się ona za paskiem stanu i paskiem nawigacji, ponieważ jest również ustawiony kolor paska nawigacji.

cel: zmiana paska stanu i koloru paska nawigacji po otwarciu szuflady nawigacji i przywrócenie jej z powrotem po zamknięciu.

rozwiązanie :

        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) {
        }
    });
Abhishek Garg
źródło
0

Wszystkie odpowiedzi tutaj są bardzo skomplikowane, pozwólcie, że podam prosty, prosty kod. Pod stylem AppTheme dodaj tę linię kodu, a po otwarciu szuflady otrzymasz szary półprzezroczysty pasek stanu.

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

to sprawi, że to zadziała.

Keshav Bhamaan
źródło