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
Odpowiedzi:
Tło Twojego paska stanu jest białe, czyli tło Twojej szuflady
LinearLayout
. Czemu? Jesteś ustawieniemfitsSystemWindows="true"
dla siebieDrawerLayout
i tego, co jest wLinearLayout
środku. PowodujeLinearLayout
to rozszerzenie się za pasek stanu (który jest przezroczysty). W ten sposób tło dla szuflady na pasku stanu jest białe.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
LinearLayout
i pokolorować tło zawartości w nim zawartej. Lub2) Możesz usunąć
fitsSystemWindows="true"
zLinearLayout
. 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.Jeśli chcesz, aby szuflada
ScrimInsetFrameLayout
znajdował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ć#4000
wszystko, co chcesz. Nie zapomnijfitsSystemWindows="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
LinearLayout
na 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.
źródło
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.źródło
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:background
aby 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ć.
źródło
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 :)
źródło
Musisz owinąć układ szuflady nawigacji wewnątrz pliku
ScrimLayout
.ScrimLayout
Zasadzie rysuje półprzezroczystą prostokąt nad szufladą układu nawigacji. Aby pobrać rozmiar wstawki, po prostu zastąp jąfitSystemWindows
w plikuScrimLayout
.@Override protected boolean fitSystemWindows(Rect insets) { mInsets = new Rect(insets); return true; }
Później zastąp,
onDraw
aby 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.
źródło
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>
źródło
ScrimInsetsView_insetForeground
atrybut.android.support.design.internal.ScrimInsetsFrameLayout
z biblioteki wsparciaMiał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.
źródło
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>
windowTranslucentStatus
sprawi, że pasek stanu będzie przezroczysty,windowDrawsSystemBarBackgrounds
powie mu, aby narysował cokolwiek pod nim, zamiast być czarną pustką.Zadzwoń
DrawerLayout.setStatusBarBackground()
lubDrawerLayout.setStatusBarBackgroundColor()
w swojej głównej aktywnościTo mówi DrawerLayout, aby pokolorował obszar paska stanu.
źródło
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) { } });
źródło
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.
źródło