Фон уведомлений в Android Lollipop белый. Как я могу его изменить?

Я хочу показать уведомление для сообщения в своем приложении. В предыдущих версиях Android все в порядке, но в Lollipop фон уведомлений белый. Я использовал этот XML-код для своего макета уведомлений в layout_message_notification.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_messageNotification"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_weight=".2"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent">

        <ImageView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/message_icon"/>

    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_weight=".8"
        android:layout_height="wrap_content"
        android:background="@android:color/transparent">

        <TextView
            android:id="@+id/textView_notification_title"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="right|center_vertical"
            android:layout_margin="15dp"/>

    </LinearLayout>

</LinearLayout>

Мое уведомление в леденец показано следующим образом: notification style in lollipop

Как сделать фоновое уведомление темным или прозрачным, как в предыдущих версиях Android?

Ответ 1

В конце концов, если вы хотите следовать официальной спецификации материала спецификации

Всегда используйте ресурсы стиля для текста настраиваемого уведомления

и использовать пользовательский макет ваших уведомлений, возможно, вам стоит не переопределять фон по умолчанию, а изменить стиль текста из-за версии API. Вы можете достичь этого, создав два файла стиля ресурса и используя их в соответствии с текущей версией API:

<сильные > значения-V21/notification_styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- according to official recommendation custom notifications should has the same text style as default one-->
  <style name="NotificationHeader" parent="@android:style/TextAppearance.Material.Notification.Title" />
  <style name="NotificationContent" parent="@android:style/TextAppearance.Material.Notification.Line2" />
</resources>

и

<сильные > значения /notification _styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <!-- according to official recommendation custom notifications should has the same text style as default one-->
  <style name="NotificationHeader" parent="@android:style/TextAppearance.StatusBar.EventContent.Title" />
  <style name="NotificationContent" parent="@android:style/TextAppearance.StatusBar.EventContent" />
</resources>

Ответ 2

В этом ответе описывается метод hacky изменения цвета фона уведомлений.
Обратите внимание: это недокументированное обходное решение; он основан на размышлении и может быть нарушен на пользовательских прошивках; он применим только к версиям Android Lollipop и Marshmallow; он не работает в Android N и более высоких версиях. Я бы рекомендовал придерживаться цвета по умолчанию, если у вас нет серьезных причин его избежать.

Почему
Не существует законного способа установить цвет фона для пользовательского уведомления. Google решил, что уведомления должны быть белого или светло-серого в зависимости от его приоритета в соответствии с Material Design. Однако Google также сделал два исключения из этого правила:

  • Уведомления для старых приложений отображаются с черным цветом фон;
  • Уведомления, созданные с помощью MediaStyle, могут иметь любой цвет.

В результате второго исключения такое ограничение выглядит нелогичным и необоснованным, и это единственное возможное оправдание, почему вы по-прежнему хотите использовать пользовательский цвет вместо того, который рекомендуется (или принудительно?) Google.

ЧТО ВНУТРИ
Давайте рассмотрим BaseStatusBar, чтобы узнать, как это ограничение наложено. Единственное место, где вычисляется цвет фона для уведомления, применить методColorsAndBackgrounds.
Первая ветвь оператора if предназначена для устаревших приложений. Единственный способ получить здесь - установить целевой SDK вашего приложения ниже Build.VERSION_CODES.LOLLIPOP. В этом случае фон станет черным.
Нас интересует оператор entry.row.setTintColor. Чтобы достичь этого, необходимо пройти несколько проверок, в том числе тот, который содержится в isMediaNotification method. Вот они:

  • Уведомление должно содержать как обычные, так и большие представления.
  • Макет верхнего уровня в обоих представлениях должен иметь com.android.internal.R.id.status_bar_latest_event_content как его идентификатор.
  • Большой макет должен содержать виджет с com.android.internal.R.id.media_actions в качестве его идентификатора.

КАК
Наиболее проблематичными являются идентификаторы, если они объявлены во внутренних ресурсах и не могут быть доступны из XML-макета приложения.
Вторая проблема заключается в том, что RemoteViews, используемый в уведомлении, является всего лишь идентификатором ресурса компоновки в приложении и не может быть сконструирован в коде. В результате мы не можем добавлять макеты с идентификаторами, необходимыми для прохождения всех проверок, упомянутых выше.

Однако Google добавил методы addView и removeAllViews в RemoteViews для своих нужд (они используются в уведомлении MediaStyle) и забыли сделать их частными.

Итак, окончательная идея проста:

  • создать уведомление на основе внутреннего макета, определенного Google (пройти 2-й чек)
  • удалите все с помощью removeAllViews
  • добавьте наш собственный макет с помощью addView
  • специальный случай для большого вида: добавьте макет, определенный Google, который содержит media_actions для невидимого представления внутри нашего пользовательского макета (чтобы пройти 3-й чек).

Недостатки:

  • раздувание неиспользуемых видов (они удаляются сразу после раздувания)
  • сложная и более глубокая иерархия раскладки

Решение
Наш пользовательский большой вид должен содержать FrameLayout с android.R.id.empty как его идентификатор. Фактически, любой идентификатор может использоваться здесь, просто убедитесь, что вы ссылаетесь на тот же идентификатор в коде (см. Ниже).

// We need theese ids to use internal Android resources
int topId = Resources.getSystem().getIdentifier("status_bar_latest_event_content", "id", "android");
int topBigLayout = Resources.getSystem().getIdentifier("notification_template_material_big_media_narrow", "layout", "android");
int topSmallLayout = Resources.getSystem().getIdentifier("notification_template_material_media", "layout", "android");

RemoteViews viewSmall = ...; // Create our custom view here
RemoteViews viewBig = ...; // Create our custom big view here

// This is invisible inner view - to have media_actions in hierarchy
RemoteViews innerTopView = new RemoteViews("android", topBigLayout);
viewBig.addView(android.R.id.empty, innerTopView);

// This should be on top - we need status_bar_latest_event_content as top layout
RemoteViews topBigView = new RemoteViews("android", topBigLayout);
topBigView.removeAllViews(topId);
topBigView.addView(topId, viewBig);

// This should be on top - we need status_bar_latest_event_content as top layout
RemoteViews topSmallView = new RemoteViews("android", topSmallLayout);
topSmallView.removeAllViews(topId);
topSmallView.addView(topId, viewSmall);

Notification.Builder builder = new Notification.Builder(this);

builder.setSmallIcon(R.drawable.ic_notification)
        .setTicker("Some text")
        .setColor(0xff000000) // The desired color!
        .setContent(topSmallView);

Notification n = builder.build();
n.bigContentView = topBigView;

// Use our notification "n" here as usual

Для управления высотой большого вида можно использовать другой макет вместо notification_template_material_big_media_narrow на верхнем уровне. Найдите соответствующий здесь среди файлов notification_template_xxx.xml. Но не забывайте о том, чтобы поместить media_actions в иерархию.

Ответ 3

Это поведение правильное.

Если вы хотите иметь прямой контроль над этим аспектом, я предлагаю вам сделать следующее:

  • Создайте альтернативную версию layout_message_notification.xml внутри папки res/layout-v21

  • Немного измените эту новую версию, изменив внешний цвет фона :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_messageNotification"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/gray">
//...