Lollipop: рисовать за statusBar с прозрачным цветом


144

Я установил прозрачный цвет statusBar для Lollipop только со следующей строкой в ​​моей теме:

<item name="android:statusBarColor">@android:color/transparent</item>

Теперь мне нужно нарисовать за ним, но я не могу нарисовать вид за ним. Я знаю, как это сделать со windowTranslucentStatusсвойством, но не хочу использовать это свойство, так как в этом случае цвет statusBar, установленный на transparent, будет игнорироваться.


2
Вы также можете использоватьandroid:fitsSystemWindows="true"
Aznix

Ответы:


392

Способ №1:

Чтобы получить полностью прозрачную строку состояния, вы должны использовать statusBarColor, что доступно только в API 21 и выше. windowTranslucentStatusдоступен в API 19 и выше, но добавляет тонированный фон для строки состояния. Тем не менее, установка windowTranslucentStatusделает достичь одну вещь , которая меняется statusBarColorна прозрачный не делает: он устанавливает SYSTEM_UI_FLAG_LAYOUT_STABLE и SYSTEM_UI_FLAG_LAYOUT_FULLSCREENфлаги. Самый простой способ добиться того же эффекта - установить эти флаги вручную, что эффективно отключает вставки, налагаемые системой макета Android, и оставляет вас на произвол судьбы.

Вы вызываете эту строку в своем onCreateметоде:

getWindow().getDecorView().setSystemUiVisibility(
    View.SYSTEM_UI_FLAG_LAYOUT_STABLE
    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);

Не забудьте также установить прозрачность в /res/values-v21/styles.xml:

<item name="android:statusBarColor">@android:color/transparent</item>

Или программно установите прозрачность:

getWindow().setStatusBarColor(Color.TRANSPARENT);

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

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

Способ №2:

Если вам нужно только нарисовать фоновое изображение под строкой состояния, вместо того, чтобы размещать представление за ним, это можно сделать, просто установив фон темы вашего действия на желаемое изображение и установив прозрачность строки состояния, как показано в методе # 1. Это был метод, который я использовал для создания снимков экрана для статьи об Android Police, опубликованной несколько месяцев назад.

Способ №3:

Если вам нужно игнорировать стандартные системные вставки для одних макетов, сохраняя их работоспособность в других, единственный жизнеспособный способ сделать это - работать с часто связываемым ScrimInsetsFrameLayoutклассом. Конечно, некоторые вещи, выполняемые в этом классе, необходимы не для всех сценариев. Например, если вы не планируете использовать наложение синтетической строки состояния, просто закомментируйте все в init()методе и не добавляйте ничего в файл attrs.xml. Я видел, как этот подход работает, но я думаю, вы обнаружите, что он имеет некоторые другие последствия, которые, возможно, потребует много работы.

Я также заметил, что вы против упаковки нескольких макетов. В случае обертывания одного макета внутри другого, когда оба имеют match_parentвысоту и ширину, последствия для производительности слишком тривиальны, чтобы о них беспокоиться. Тем не менее, вы можете полностью избежать этой ситуации, изменив класс, из которого он расширяется, FrameLayoutна любой другой тип класса Layout, который вам нравится. Он будет работать нормально.


9
Большое спасибо, настройка SYSTEM_UI_FLAG_LAYOUT_STABLEи SYSTEM_UI_FLAG_LAYOUT_FULLSCREENфлаги было решение , которое я искал.
alxscms

3
Я месяц искал твое первое решение. Спасибо!
NeoKree

1
Здравствуй. Ваш код отлично работает. Но когда я выхожу из фрагмента, как мне откатить изменения?
TheOnlyAnil 02

3
Это не работает с CollapsingToolbarLayoutи Toolbar: строка состояния остается не полностью прозрачной и android:statusBarColorне действует
Константин Конопко

Спасибо. Если вы затем захотите вернуться к не полноэкранному режиму, прочтите developer.sonymobile.com/knowledge-base/tutorials/… .
CoolMind

45

Это сработало в моем случае


// Create/Set toolbar as actionbar
toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

// Check if the version of Android is Lollipop or higher
if (Build.VERSION.SDK_INT >= 21) {

    // Set the status bar to dark-semi-transparentish
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
            WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

    // Set paddingTop of toolbar to height of status bar.
    // Fixes statusbar covers toolbar issue
    toolbar.setPadding(0, getStatusBarHeight(), 0, 0);
}

// A method to find height of the status bar
public int getStatusBarHeight() {
    int result = 0;
    int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

Для получения дополнительной информации о работе со статусбарами: youtube.com/watch?v=_mGDMVRO3iE



@TheOnlyAnil определяет фиксированную высоту панели инструментов :) "56dp" или около того должно выглядеть хорошо
Майкл

4
Получение такой высоты строки состояния полностью не поддерживается, недокументировано и наверняка сломается в будущих версиях. Вместо этого используйте fitsSystemWindows = "true".
Грег Эннис,

1
Ну, я тоже так делаю, потому что иногда тебе просто нужно.
Грег Эннис

2
Это очень плохая идея, согласно Крису Бэйнсу (инженер Google) в
своем

1
@Michael FWIW, я делал это в прошлом, и API WindowInsets (метод) для этого «должен быть простым и выполняться без необходимости думать об этом», но это не так, это хромой бесполезный API, который добавляет много ненужных накладных расходов. Google всегда готов на 80%, а последние 20% оставляют разработчикам ... которые взламывают все до тех пор, пока это не произойдет. ¯_ (ツ) _ / ¯
Мартин Маркончини

21

Попробуйте эту тему

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- Customize your theme here. -->
    <item name="windowActionBar">false</item>
    <item name="windowNoTitle">true</item>
    <item name="colorPrimaryDark">@android:color/transparent</item>
    <item name="colorPrimary">@color/md_blue_200</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowTranslucentStatus">true</item>
</style>

Убедитесь, что ваш макет установлен

android:fitsSystemWindows="false"

4
настройка android:fitsSystemWindows="false"разрезает панель инструментов пополам.
Хаммад Насир

16

Вместо того

<item name="android:statusBarColor">@android:color/transparent</item>

Используйте следующее:

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

И не забудьте удалить верхний отступ (который добавлен по умолчанию) в макете MainActivity.

Обратите внимание, что это не делает строку состояния полностью прозрачной, и на строке состояния все равно будет «блеклый черный» наложение.


2
При использовании <item name="android:windowTranslucentStatus">true</item>он заставляет Android помещать блеклый черный фон под строку состояния, чего я не ищу. Как бы то ни было, чтобы не было этого черного выцветшего фона?
alxscms

1
Я много чего пробовал, но не могу найти способ удалить блеклый черный фон. Поэтому я завершаю свое исследование, говоря, что это невозможно на Android (даже не с ScrimLayout). В этом есть смысл, потому что он обеспечивает правильную читаемость строки состояния в любое время. Я обновлю свой ответ выше.
Jeroen Mols

Я думаю, было бы очень странно иметь возможность поместить прозрачный цвет в строку состояния, не имея возможности рисовать за ним. Я понимаю, почему вы говорите, что имеет смысл, что вы не можете удалить этот черный потускневший фон, но определенно не так уж сложно быть достаточно осторожным, чтобы значок строки состояния оставался видимым. Вот статья полиции Android, показывающая использование прозрачной строки состояния androidpolice.com/2014/07/04/… . Постараюсь связаться с автором.
alxscms

1
Надеюсь, ты сможешь с ним связаться. Я тоже очень заинтересовался этим :)
Jeroen Mols

Большой! Обновил и мой ответ.
Jeroen Mols

10

Решение от Cody Toombs почти помогло мне. Я не уверен, связано ли это с Xamarin или нет, но теперь у меня есть приемлемое решение:

пример

Это моя установка:

У меня есть проект Android, в котором я сослался на пакеты Android.Support v4 и v7. У меня определены два стиля:

значения / styles.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <style name="MyStyle" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
    </style>
</resources>

значения-v21 / styles.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<resources>
    <style name="MyStyle" parent="@style/Theme.AppCompat.Light.NoActionBar">
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
</resources>

AndroidManifest нацелен на MyStyle:

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.agn.test.test">
    <uses-sdk android:minSdkVersion="10" />
    <application android:allowBackup="true" android:icon="@mipmap/icon" android:label="@string/app_name" android:theme="@style/MyStyle">
    </application>
</manifest>

И, наконец, код в основном действии:

[Activity (Label = "Test", MainLauncher = true, Icon = "@mipmap/icon")]
public class MainActivity : Activity
{
    protected override void OnCreate (Bundle savedInstanceState)
    {
        base.OnCreate (savedInstanceState);

        SetContentView (Resource.Layout.Main);
        //Resource.Layout.Main is just a regular layout, no additional flags. Make sure there is something in there like an imageView, so that you can see the overlay.

        var uiOptions = (int)Window.DecorView.SystemUiVisibility;
        uiOptions ^= (int)SystemUiFlags.LayoutStable;
        uiOptions ^= (int)SystemUiFlags.LayoutFullscreen;
        Window.DecorView.SystemUiVisibility = (StatusBarVisibility)uiOptions;

        Window.AddFlags (WindowManagerFlags.DrawsSystemBarBackgrounds);
    }
}

Обратите внимание, что я установил флаг DrawsSystemBarBackgrounds, в этом вся разница

Window.AddFlags (WindowManagerFlags.DrawsSystemBarBackgrounds); 

Я потратил много времени на то, чтобы все исправить, на самом деле, слишком много времени. Надеюсь, этот ответ поможет любому, кто пытается добиться того же.


1
android:windowTranslucentStatusтребуется уровень API 19 или выше, как это возможно, что вы используете его в своих values ​​/ styles.xml , имея android:minSdkVersion10?
mradzinski

7

Ответ @Cody Toombs приводит к проблеме, из-за которой макет находится за панелью навигации. Итак, я обнаружил, что использую это решение, данное @Kriti

вот фрагмент кода Kotlin для того же:

if (Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
    setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, true)
}

if (Build.VERSION.SDK_INT >= 19) {
    window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
}

if (Build.VERSION.SDK_INT >= 21) {
    setWindowFlag(this, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, false)
    getWindow().setStatusBarColor(Color.TRANSPARENT)
}

private fun setWindowFlag(activity: Activity, bits: Int, on: Boolean) {

    val win: Window = activity.getWindow()

    val winParams: WindowManager.LayoutParams = win.getAttributes()
    if (on) {
        winParams.flags = winParams.flags or bits
    } else {
        winParams.flags = winParams.flags and bits.inv()
    }
    win.setAttributes(winParams)
}

Вам также необходимо добавить

android:fitsSystemWindows="false"

корневой вид вашего макета.


3

У меня была такая же проблема, поэтому я создаю ImageView, который рисует за API строки состояния 19+

Установите собственное изображение за строкой состояния gist.github.com

public static void setTransparent(Activity activity, int imageRes) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        return;
    }
    // set flags
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
    } else {
        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }

    // get root content of system window
    //ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
    // rootView.setFitsSystemWindows(true);
    // rootView.setClipToPadding(true);

    ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
    if (contentView.getChildCount() > 1) {
        contentView.removeViewAt(1);
    }

    // get status bar height
    int res = activity.getResources().getIdentifier("status_bar_height", "dimen", "android");
    int height = 0;
    if (res != 0)
        height = activity.getResources().getDimensionPixelSize(res);

    // create new imageview and set resource id
    ImageView image = new ImageView(activity);
    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
    image.setLayoutParams(params);
    image.setImageResource(imageRes);
    image.setScaleType(ScaleType.MATRIX);

    // add image view to content view
    contentView.addView(image);
    // rootView.setFitsSystemWindows(true);

}

2

Вы можете использовать ScrimInsetFrameLayout

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/widget/ScrimInsetsFrameLayout.java

android: fitsSystemWindows = "true" должен быть установлен в макете холста!


Я видел этот класс, но что, если я хочу использовать RelativeLayoutвместо FrameLayout?
alxscms

3
использовать относительный макет внутри макета фрейма.
Муртаза Хуршид Хуссейн

2
Вложение макетов в макеты определенно не решение для меня относительно производительности
alxscms

1
производительность приложения зависит от многих факторов, которые могут отличаться. если у вас нет выхода, попробуйте. Лучшая удача
Муртаза Хуршид Хуссейн

Я понимаю, что вы имеете в виду, но должно быть другое решение, иначе может быть способ реализовать в любом макете то, что было реализовано в этом пользовательском макете фрейма. Но в любом случае спасибо
alxscms

1

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

if (Build.VERSION.SDK_INT >= 21) {
    getWindow().setStatusBarColor(Color.TRANSPARENT);
    FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) toolbar.getLayoutParams();
    lp.setMargins(0, -getStatusBarHeight(), 0, 0);
}

И я использовал на панели инструментов и в корневом представлении

android:fitsSystemWindows="true"

1
getStatusBarHeight () не является стандартным
Роэл

Этот метод реализован в решении от @ mike-milla ... public int getStatusBarHeight () {int result = 0; int resourceId = getResources (). getIdentifier ("status_bar_height", "dimen", "android"); если (resourceId> 0) {результат = getResources (). getDimensionPixelSize (resourceId); } вернуть результат; }
jegumi

Спасибо. Я сделал так: AppBarLayout.LayoutParams lp = (AppBarLayout.LayoutParams) toolbar.getLayoutParams (); lp.topMargin + = getStatusBarHeight ();
CoolMind

1

Есть хорошая библиотека StatusBarUtil от @laobie, которая помогает легко рисовать изображения в StatusBar.

Просто добавьте в свой build.gradle:

compile 'com.jaeger.statusbarutil:library:1.4.0'

Затем в наборе Activity

StatusBarUtil.setTranslucentForImageView(Activity activity, int statusBarAlpha, View viewNeedOffset)

В макете

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <ImageView
        android:layout_alignParentTop="true"
        android:layout_width="match_parent"
        android:adjustViewBounds="true"
        android:layout_height="wrap_content"
        android:src="@drawable/toolbar_bg"/>

    <android.support.design.widget.CoordinatorLayout
        android:id="@+id/view_need_offset"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/transparent"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>

        <!-- Your layout code -->
    </android.support.design.widget.CoordinatorLayout>
</RelativeLayout>

Для получения дополнительной информации загрузите демо или клон со страницы github и поиграйте со всеми функциями.

Примечание. Поддержите KitKat и выше.

Надеюсь, это поможет кому-то другому!


1

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

  1. Цвет фона : для SDK 21+, как упоминалось во многих ответах, <item name="android:windowTranslucentStatus">true</item> строка состояния будет прозрачной и отображаться перед пользовательским интерфейсом. Ваша Activity займет все пространство вверху.
  2. Цвет фона : опять же, для SDK 21+ <item name="android:statusBarColor">@color/your_color</item>просто придаст цвет вашей строке состояния, не влияя ни на что другое.

  3. Однако на более поздних устройствах (Android M / +) значки стали иметь разные оттенки. ОС может придать более темный оттенок серого значкам для SDK 23 / +, если вы переопределите свой styles.xmlфайл в values-23папке и добавите <item name="android:windowLightStatusBar">true</item>.
    Таким образом, вы предоставите своему пользователю более заметную строку состояния, если ваша строка состояния имеет светлый цвет (подумайте о том, как многие приложения Google имеют светлый фон, а значки там отображаются сероватым цветом).
    Я бы посоветовал вам использовать это, если вы придаете цвет своей строке состояния через пункт 2.

  4. В самых последних устройствах SDK 29 / + поставляется с общесистемной светлой и темной темой, управляемой пользователем. Как разработчики, мы также должны переопределить наш файл стиля в новой values-nightпапке, чтобы дать пользователю два разных опыта.
    Здесь я снова обнаружил, что пункт №2 эффективен в обеспечении «цвета фона для строки состояния». Но система не меняла цвет значков строки состояния для моего приложения. поскольку моя дневная версия стиля состояла из более легкой темы, это означает, что пользователи будут страдать от плохой видимости (белые значки на более светлом фоне).
    Эту проблему можно решить, используя подход пункта 3 или переопределив файл стиля в values-29папке и используя более новый api <item name="android:enforceStatusBarContrast">true</item>. Это автоматически приведет к сероватому оттенку значков, если цвет вашего фона слишком светлый.


0

В Android Studio 1.4 проект шаблона со стандартным кодом устанавливает Overlayтему на AppbarLayout и / или на панели инструментов. Они также настроены на отображение за строкой состояния с помощью fitSystemWindowattribute = true. Это приведет к тому, что только панель инструментов будет отображаться непосредственно под строкой состояния, а все остальное будет отображаться под панелью инструментов. Таким образом, приведенные выше решения не будут работать сами по себе. Вам нужно будет внести следующие изменения.

  • Удалите тему наложения или измените ее на тему без наложения для панели инструментов.
  • Поместите следующий код в свой файл styles-21.xml .

    @android: цвет / прозрачный

  • Назначьте эту тему действию, содержащему панель навигации в файле AndroidManifest.xml .

Это заставит панель навигации отображаться за прозрачной строкой состояния.


0

Все, что вам нужно сделать, это установить эти свойства в своей теме.

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

1
Я почти уверен, что это приведет к тому, что ваш макет попадет под кнопки навигации пользовательского интерфейса (устройства с экранной навигацией). Это сделало нижнюю навигацию недоступной в нашем приложении, поэтому мы отказались от этих флагов после тестирования на Nexus
EpicPandaForce

0

Принятый ответ сработал для меня с использованием CollapsingToolbarLayout. Однако важно отметить, что это setSytstemUiVisibility()отменяет любые предыдущие вызовы этой функции. Так что, если вы используете эту функцию где - то еще для одной и той же точки зрения, необходимо включить View.SYSTEM_UI_FLAG_LAYOUT_STABLEи View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREENфлаги, или они будут переопределены с новым вызовом.

Так было со мной, и как только я добавил два флага в другое место, куда звонил setSystemUiVisibility(), принятый ответ сработал отлично.


-1

Вот тема, которую я использую для этого:

<style name="AppTheme" parent="@android:style/Theme.NoTitleBar">

    <!-- Default Background Screen -->
    <item name="android:background">@color/default_blue</item>
    <item name="android:windowTranslucentStatus">true</item>

</style>

1
Есть несколько странных отрицателей. Даже если решение было правильным для вас и кого-то другого, они проголосуют против, вместо того, чтобы переходить к другому ответу.
CoolMind

Все, о чем я прошу, - это объяснения. Я не думаю, что люди должны иметь возможность голосовать против без объяснения причин.
Droid Chris

Я тоже так думаю.
CoolMind

Я объясню, что в этом пакете опция фона работает некорректно. Исправьте, пожалуйста, свой ответ.
Морозов

Хммм ... в какой версии Android вы это делаете? Я использую эту конфигурацию в Android M.
Дроид Крис

-3

Правильное решение - изменить свойство в XML в теге Activity на стиль ниже. Это просто работает

  android:theme="@style/Theme.AppCompat.Light.NoActionBar"
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.