Android Lollipop 中的通知背景为白色。我该如何改变它?
Notification background in Android Lollipop is white. How can I change it?
我想在我的应用程序中显示消息通知。在 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?
这是正确的通知行为。
如果您想直接控制这方面,我建议您执行以下操作:
在文件夹 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">
//...
最后,如果你喜欢遵循官方material设计specification
Always use style resources for the text of a custom notification
并使用通知的自定义布局,您可能会考虑不覆盖默认背景,而是根据 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>
此答案描述了一种 hacky 更改通知背景颜色的方法。
注意:这是一个 未记录的解决方法 ;它基于反射,可能会在自定义固件上被破坏;它仅适用于 Android Lollipop 和 Marshmallow 版本;它 将无法在 Android N 和更高版本上工作 。我建议坚持使用默认颜色,除非您有充分的理由避免使用它。
为什么
没有合法的方法来为自定义通知设置背景颜色。 Google 根据 Material 设计 决定通知必须是白色或浅灰色,具体取决于其优先级。但是,Google 也对这条规则做了两个例外:
- 旧应用程序的通知显示为黑色
背景;
- 使用
MediaStyle
创建的通知可以是任何颜色。
由于第二个例外,这样的限制看起来不合逻辑且没有道理,这是您仍然想使用自定义颜色而不是 [推荐(或强制?)的颜色的唯一可能借口=110=].
里面有什么
让我们看看 BaseStatusBar
看看这个限制是如何施加的。计算通知背景颜色的唯一地方是 applyColorsAndBackgrounds method.
if
语句的第一个分支用于遗留应用程序。到达此处的唯一方法是在 Build.VERSION_CODES.LOLLIPOP
下面设置您的应用程序的目标 SDK。这种情况下背景会变黑。
我们对 entry.row.setTintColor
语句感兴趣。要达到它,应该通过几项检查,包括 isMediaNotification method 中包含的一项。他们在这里:
- 通知必须包含常规视图和大视图。
- 两个视图中的顶级布局必须以
com.android.internal.R.id.status_bar_latest_event_content
作为其 ID。
- 大布局必须包含一个以
com.android.internal.R.id.media_actions
作为其 ID 的小部件。
如何
问题最大的是 ID,只要它们是在内部资源中声明的,并且不能从应用程序的布局中访问 XML.
第二个问题是通知中使用的RemoteViews
只是应用程序内部布局资源的ID,无法在代码中构造。因此,我们无法添加具有通过上述所有检查所需 ID 的布局。
但是,Google 将 addView
和 removeAllViews
方法添加到 RemoteViews
中以满足他们的需要(它们在 MediaStyle
通知中使用)并且忘记了将它们设为私有。
所以,最后的想法很简单:
- 根据Google定义的内部布局构建通知(以通过第二次检查)
- 删除所有
removeAllViews
- 使用
addView
添加我们的自定义布局
- 大视图的特殊情况:将包含
media_actions
的 Google 定义的布局添加到我们自定义布局内的不可见视图(以通过第 3 次检查)
缺点:
- 膨胀未使用的视图(它们在膨胀后立即被删除)
- 复杂且更深的布局层次
解决方案
我们的自定义大视图必须包含 FrameLayout
和 android.R.id.empty
作为其 ID。实际上,这里可以使用任何 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
放入层次结构中。
我想在我的应用程序中显示消息通知。在 Android 之前的版本中一切正常,但在 Lollipop 中通知背景为白色。
我在 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>
我在棒棒糖中的通知是这样显示的:
如何使通知背景变暗或透明,就像以前版本的 Android?
这是正确的通知行为。
如果您想直接控制这方面,我建议您执行以下操作:
在文件夹
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"> //...
最后,如果你喜欢遵循官方material设计specification
Always use style resources for the text of a custom notification
并使用通知的自定义布局,您可能会考虑不覆盖默认背景,而是根据 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>
此答案描述了一种 hacky 更改通知背景颜色的方法。
注意:这是一个 未记录的解决方法 ;它基于反射,可能会在自定义固件上被破坏;它仅适用于 Android Lollipop 和 Marshmallow 版本;它 将无法在 Android N 和更高版本上工作 。我建议坚持使用默认颜色,除非您有充分的理由避免使用它。
为什么
没有合法的方法来为自定义通知设置背景颜色。 Google 根据 Material 设计 决定通知必须是白色或浅灰色,具体取决于其优先级。但是,Google 也对这条规则做了两个例外:
- 旧应用程序的通知显示为黑色 背景;
- 使用
MediaStyle
创建的通知可以是任何颜色。
由于第二个例外,这样的限制看起来不合逻辑且没有道理,这是您仍然想使用自定义颜色而不是 [推荐(或强制?)的颜色的唯一可能借口=110=].
里面有什么
让我们看看 BaseStatusBar
看看这个限制是如何施加的。计算通知背景颜色的唯一地方是 applyColorsAndBackgrounds method.
if
语句的第一个分支用于遗留应用程序。到达此处的唯一方法是在 Build.VERSION_CODES.LOLLIPOP
下面设置您的应用程序的目标 SDK。这种情况下背景会变黑。
我们对 entry.row.setTintColor
语句感兴趣。要达到它,应该通过几项检查,包括 isMediaNotification method 中包含的一项。他们在这里:
- 通知必须包含常规视图和大视图。
- 两个视图中的顶级布局必须以
com.android.internal.R.id.status_bar_latest_event_content
作为其 ID。 - 大布局必须包含一个以
com.android.internal.R.id.media_actions
作为其 ID 的小部件。
如何
问题最大的是 ID,只要它们是在内部资源中声明的,并且不能从应用程序的布局中访问 XML.
第二个问题是通知中使用的RemoteViews
只是应用程序内部布局资源的ID,无法在代码中构造。因此,我们无法添加具有通过上述所有检查所需 ID 的布局。
但是,Google 将 addView
和 removeAllViews
方法添加到 RemoteViews
中以满足他们的需要(它们在 MediaStyle
通知中使用)并且忘记了将它们设为私有。
所以,最后的想法很简单:
- 根据Google定义的内部布局构建通知(以通过第二次检查)
- 删除所有
removeAllViews
- 使用
addView
添加我们的自定义布局
- 大视图的特殊情况:将包含
media_actions
的 Google 定义的布局添加到我们自定义布局内的不可见视图(以通过第 3 次检查)
缺点:
- 膨胀未使用的视图(它们在膨胀后立即被删除)
- 复杂且更深的布局层次
解决方案
我们的自定义大视图必须包含 FrameLayout
和 android.R.id.empty
作为其 ID。实际上,这里可以使用任何 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
放入层次结构中。