Gaveta de navegação semitransparente sobre a barra de status não funciona

86

Estou trabalhando no projeto Android e implementando o Navigation Drawer. Estou lendo as novas especificações de design de material e a lista de verificação de design de material .
A especificação diz que o painel deslizante deve flutuar acima de tudo, incluindo a barra de status, e ser semitransparente sobre a barra de status.

Meu painel de navegação está sobre a barra de status, mas não tem nenhuma transparência. Segui o código deste post SO, conforme sugerido no blog de desenvolvedores do Google, link acima. Como faço para usar DrawerLayout para exibir na ActionBar / Toolbar e na barra de status? .

Abaixo está o meu layout 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>

Abaixo está o tema do meu aplicativo

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

Abaixo está o tema do meu apps 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>

Abaixo está o meu método 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);
    }

Abaixo está uma captura de tela da minha gaveta de navegação mostrando que a parte superior não é semitransparente

Captura de tela mostrando não transparente na barra de status

Boardy
fonte
Poste uma captura de tela de seus resultados.
Alok Nair
@Boardy, você conseguiu fazê-lo funcionar?
Michele La Ferla
Incapaz de definir o efeito Transparente na barra de status. Livre-se dele.
Ronak Gadhia,

Respostas:

103

O fundo da sua barra de status é branco, o fundo da sua gaveta LinearLayout. Por quê? Vocês são configurações fitsSystemWindows="true"para você DrawerLayoute para LinearLayoutdentro dele. Isso faz com que seu LinearLayoutse expanda atrás da barra de status (que é transparente). Assim, torna o fundo da gaveta parte da barra de status branco.

insira a descrição da imagem aqui
Se você não quiser que sua gaveta se estenda para trás da barra de status (deseja ter um fundo semitransparente para toda a barra de status), você pode fazer duas coisas:

1) Você pode simplesmente remover qualquer valor de fundo de seu LinearLayoute colorir o fundo de seu conteúdo dentro dele. Ou

2) Você pode remover fitsSystemWindows="true"do seu LinearLayout. Acho que essa é uma abordagem mais lógica e mais limpa. Você também evitará que uma sombra seja projetada sob a barra de status, onde sua gaveta de navegação não se estende.

insira a descrição da imagem aqui
Se você deseja que sua gaveta se estenda atrás da barra de status e tenha uma sobreposição semitransparente do tamanho da barra de status, você pode usar um ScrimInsetFrameLayoutcomo um contêiner para o conteúdo da sua gaveta ( ListView) e definir o plano de fundo da barra de status usando app:insetForeground="#4000". Claro, você pode mudar #4000para o que quiser. Não se esqueça de ficar fitsSystemWindows="true"aqui!

Ou se você não quiser sobrepor seu conteúdo e exibir apenas uma cor sólida, você pode apenas definir o plano de fundo de sua LinearLayoutpreferência. Não se esqueça de definir o plano de fundo do seu conteúdo separadamente!

EDIT: Você não precisa mais lidar com nada disso. Consulte a Biblioteca de Suporte de Design para uma implementação de Gaveta / Visualização de Navegação mais fácil.

xip
fonte
Obrigado pela ajuda. Desculpem a demora em voltar, ando muito ocupada. Eu configurei outra recompensa para recompensar sua resposta como originalmente configurei, mas terminou antes que eu tivesse tempo de implementar suas sugestões. Tenho que esperar 23 horas, infelizmente, então vou adicioná-lo assim que puder
Boardy
Cuidado! Com as opções 1 ou 2, certifique-se de observar seus overdraws (você pode verificá-los nas opções de desenvolvimento nas configurações do telefone). Quando eu apenas defini o fundo do conteúdo dentro, tudo atrás da gaveta também estava sendo desenhado. De outra forma, ótima resposta, definitivamente me economizou algum tempo :)
Daiwik Daarun
1
Você pode explicar qual solução precisamos usar usando a biblioteca de suporte de design? Estou tendo alguns problemas, mesmo com a resposta postada por Chris Banes.
mDroidd
29

Tudo que você precisa fazer é usar alguma cor semitransparente para a barra de status. Adicione estas linhas ao seu tema v21:

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

Não se esqueça de que o color(recurso) deve sempre estar no formato de #AARRGGBB. Isso significa que a cor também incluirá o valor alfa.

mroczis
fonte
Você é o cara, mas ... Pode me dizer por que isso só funciona na Activity que tem Gaveta? Os outros, aqueles que não o fizeram, mostram a barra de status cinza.
Joaquin Iurchuk
15

Por que não usar algo semelhante a isso?

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

O código acima usa o recurso alfa no android:backgroundpara poder mostrar transparência na barra de ações.

Existem outras maneiras de fazer isso por meio de código, como mostram outras respostas. Minha resposta acima, faz o necessário no arquivo de layout xml, que na minha opinião é facilmente editado.

Michele La Ferla
fonte
4
Por que não, mas não está claro o que você está mudando e como isso responde à pergunta.
rds
defina o divisor para códigos alfa transparentes e usados ​​na cor de fundo
Michele La Ferla
9

Todas as respostas mencionadas aqui são muito antigas e longas. A melhor e mais curta solução que funciona com o mais recente 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);

    }
}

isso vai mudar a cor da sua barra de status para transparente quando você abrir a gaveta

Agora, ao fechar a gaveta, você precisa alterar a cor da barra de status novamente para escuro. Portanto, você pode fazer isso desta forma.

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

e, em seguida, no layout principal, adicione uma única linha, ou seja,

            android:fitsSystemWindows="true"

e o layout da sua gaveta ficará como

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

e sua visualização de navegação parecerá

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

Eu testei e está funcionando perfeitamente. Espero que ajude alguém. Esta pode não ser a melhor abordagem, mas funciona bem e é simples de implementar. Marque se ajudar. Boa codificação :)

Harry Sharma
fonte
Obrigado! Isso me economizou muito tempo. Está funcionando perfeitamente com o NavigationView mais recente. Para quem não consegue fazer isso funcionar, certifique-se de escolher uma cor com alguma transparência para o "colorPrimaryDarktrans", caso contrário, você não verá nenhuma mudança.
Rui de
Funciona perfeitamente, a única desvantagem é que a barra de status 'pisca' quando onDrawerClosed é chamado, mas se você definir a cor transparente colorPrimaryDarktrans para # 05000000 é quase imperceptível
Kirill Karmazin
8

Você precisa envolver o layout da gaveta de navegação dentro de um ScrimLayout.

A ScrimLayoutbasicamente desenha um retângulo semitransparente sobre o layout da gaveta de navegação. Para recuperar o tamanho da inserção, simplesmente substitua fitSystemWindowsem seu ScrimLayout.

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

Substituição posterior onDraw para desenhar um retângulo semitransparente.

Um exemplo de implementação pode ser encontrado na fonte do Google IO App. Aqui você pode ver como ele é usado no layout xml.

Markus oi
fonte
5

Se desejar, o painel de navegação está sobre a barra de status e semitransparente sobre a barra de status. Google I / O Android App Source oferece uma boa solução ( APK na Play Store não é atualizado para a última versão)

Primeiro você precisa de um 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);
    }
}

Então, em seu layout XML, altere esta parte

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

Altere LinearLayout para ScrimInsetFrameLayout assim

<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
fonte
Oi, obrigado pela resposta .. Não consigo encontrar isso, de onde temos OnInsetsCallback ... Obrigado desde já.
Ashokchakravarthi Nagarajan de
você pode colocar o ScrimInsetsView_insetForegroundatributo.
reegan29,
1
Uso android.support.design.internal.ScrimInsetsFrameLayoutda biblioteca de suporte
Romano
4

Tive um recurso semelhante para implementar em meu projeto. E para deixar minha gaveta transparente, eu só uso a transparência para cores hexadecimais . Usando 6 dígitos hexadecimais, você tem 2 dígitos hexadecimais para cada valor de vermelho, verde e azul, respectivamente. mas se você colocar dois dígitos adicionais (8 dígitos hexadecimais), você terá ARGB (dois dígitos para o valor alfa) ( veja aqui ).

Aqui estão os valores de opacidade hexadecimal: para os 2 dígitos adicionais

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

Por exemplo: se você tem a cor hexadecimal (# 111111) basta colocar um dos valores de opacidade para obter transparência. assim: (# 11111100)

Minha gaveta não cobre a barra de ação (como a sua) mas a transparência pode ser aplicada nesses casos também. Este é meu código:

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

E aqui está outro artigo que pode ajudá-lo a entender os códigos alfa em cores hexadecimais.

Fdsilva
fonte
1

As soluções existentes estão bastante desatualizadas. Aqui está a solução mais recente que deve funcionar perfeitamente em bibliotecas AndroidX ou de materiais.

  • Adicione android:fitsSystemWindows="true"à visualização raiz do seu layout, que é DrawerLayout para o seu caso.

    Isso diz ao modo de exibição para usar a tela inteira para medição e layout, sobrepondo-se à barra de status.

  • Adicione o seguinte ao seu estilo XML

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

    windowTranslucentStatustornará a barra de status transparente,
    windowDrawsSystemBarBackgroundsinforma a barra de status para desenhar qualquer coisa abaixo dela, em vez de ser um vazio preto.

  • Ligue DrawerLayout.setStatusBarBackground()ou DrawerLayout.setStatusBarBackgroundColor()em sua atividade principal

    Isso diz ao DrawerLayout para colorir a área da barra de status.

Brown Smith
fonte
0

Acima de umas boas soluções, mas não funcionaram no meu caso,

meu caso: a barra de status tem cor amarela definida já usando estilos, não é translúcida. e a cor da minha gaveta de navegação é branco, agora sempre que eu desenho minha gaveta de navegação, ela está sempre atrás da barra de status e da barra de navegação também, já que a cor da barra de navegação também é definida.

objetivo: alterar a barra de status e a cor da barra de navegação quando a gaveta de navegação for aberta e restaurá-la quando for fechada.

solução:

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

Todas as respostas aqui são muito complicadas, deixe-me fornecer um código simples e direto. No estilo AppTheme, adicione esta linha de código e você obterá uma barra de status cinza semitransparente ao abrir a gaveta.

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

isso fará com que funcione.

Keshav Bhamaan
fonte