【问题标题】:Notification background in Android Lollipop is white. How can I change it?Android Lollipop 中的通知背景是白色的。我怎样才能改变它?
【发布时间】:2015-03-26 00:12:05
【问题描述】:

我想在我的应用中显示消息通知。在以前的 Android 版本中一切正常,但在 Lollipop 中,通知背景是白色的。 我在layout_message_notification.xml 中将此 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>

我在棒棒糖中的通知显示如下:

如何使通知背景变暗或透明,就像以前的 Android 版本一样?

【问题讨论】:

    标签: android background notifications android-5.0-lollipop transparent


    【解决方案1】:

    此答案描述了一种更改通知背景颜色的hacky方法。
    注意:这是一个未记录的解决方法;它基于反射,可能会在自定义固件上被破坏;仅适用于 Android Lollipop 和 Marshmallow 版本;它无法在 Android N 及更高版本上运行。我建议您坚持使用默认颜色,除非您有充分的理由避免使用它。

    为什么
    没有为自定义通知设置背景颜色的合法方法。根据Material Design,Google 决定通知必须是白色或浅灰色,具体取决于其优先级。不过,谷歌也对这条规则做了两个例外:

    1. 旧应用程序的通知以黑色显示 背景;
    2. 使用MediaStyle 创建的通知可以是任何颜色。

    由于第二个例外,这种限制看起来不合逻辑且不合理,这是您仍想使用自定义颜色而不是 Google 推荐(或强制?)的唯一可能的借口。

    里面有什么
    让我们看看BaseStatusBar 看看这个限制是如何施加的。唯一计算通知背景颜色的地方是applyColorsAndBackgrounds method
    if 语句的第一个分支用于遗留应用程序。到达这里的唯一方法是将应用程序的目标 SDK 设置在Build.VERSION_CODES.LOLLIPOP 下方。在这种情况下,背景将变为黑色。
    我们对entry.row.setTintColor 声明感兴趣。要达到它,应该通过几项检查,包括包含在isMediaNotification method 中的一项。他们在这里:

    1. 通知必须同时包含常规视图和大视图。
    2. 两个视图中的顶级布局都必须以 com.android.internal.R.id.status_bar_latest_event_content 作为其 ID。
    3. 大布局必须包含一个 ID 为 com.android.internal.R.id.media_actions 的小部件。

    怎么做
    最有问题的是 ID,只要它们在内部资源中声明并且不能从应用程序的布局 XML 访问。
    第二个问题是通知中使用的RemoteViews只是应用内布局资源的一个ID,无法在代码中构造。因此,我们无法添加具有通过上述所有检查所需的 ID 的布局。

    但是,Google 将addViewremoveAllViews 方法添加到RemoteViews 以满足他们的需要(它们在MediaStyle 通知中使用)并且忘记将它们设为私有。

    所以,最终的想法很简单:

    • 根据 Google 定义的内部布局构建通知(以通过第二次检查)
    • 使用removeAllViews 删除所有内容
    • 使用addView 添加我们的自定义布局
    • 大视图的特殊情况:将包含 media_actions 的 Google 定义的布局添加到我们自定义布局内的不可见视图(以通过第三次检查)

    缺点:

    • 膨胀未使用的视图(膨胀后立即删除)
    • 复杂且更深的布局层次结构

    解决方案
    我们的自定义大视图必须包含FrameLayout,其ID 为android.R.id.empty。实际上,这里可以使用任何 ID,只要确保您在代码中引用相同的 ID(见下文)。

    // 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 文件中搜索合适的here。但不要忘记将media_actions 放入层次结构中。

    【讨论】:

      【解决方案2】:

      最后,如果你喜欢关注官方材料设计specification

      始终为自定义通知的文本使用样式资源

      并使用通知的自定义布局,您可能会考虑不覆盖默认背景,而是根据 API 版本更改文本样式。您可以通过创建两个资源样式文件并根据当前 API 版本使用它们来实现:

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

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

      【讨论】:

      • 可以,但是如何设置通知的背景与主题相同呢?您提供的内容仅设置通知标题和正文中的文本。自定义通知的背景在 Lollipop 中仍然是白色的。需要用 android:background 设置吗?
      【解决方案3】:

      这是通知的正确行为。

      如果您想直接控制这方面,我建议您执行以下操作:

      • 在文件夹res/layout-v21 内创建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/gray">
      //...
      

      【讨论】:

      • 是的,我终于做到了!但我希望找到更好的解决方案!谢谢。
      • 在这里分配背景颜色不是一个好主意,因为:1. 它将覆盖通知行的圆角(不适用于 HTC - 他们改变了这种风格) 2. 在调整通知的大小时仍然可以看到带有白色的通知行布局。在锁定屏幕上尤其明显。即使没有大的通知视图。
      猜你喜欢
      • 2017-12-29
      • 2016-11-08
      • 2022-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-12-11
      • 2021-05-02
      • 2015-05-15
      相关资源
      最近更新 更多