Custom notification layouts and text colors

My application shows some notifications, and depending on user preferences it might use a custom layout in a notification. It works well, but there is a small problem -- text colors. Stock Android and almost all manufacturer skins use black text against a light background for notification text, but Samsung doesn't: their notification pulldown has a dark background and the text in the default notification layout is white.

So this causes a problem: the notifications that don't use any fancy layouts show up fine, but the one that uses a custom layout is hard to read because the text is black instead of the default white. Even the official documentation just sets a #000 color for a TextView, so I couldn't find any pointers there.

A user was kind enough to take a screenshot of the problem:

Screenshot

So how do I use the default notification text color from the device in my layouts? I'd rather not start dynamically altering the text color based on phone model, since that requires a lot of updating and people with custom ROM's might still get the problem, depending on the skin they're using.

67763 次浏览

You should use the colors specified in android.R.color

For example: android.R.color.primary_text_light

Custom ROM developers and Android skin designers are supposed to update these so your app's colors can be in line with the rest of the system. This includes making sure your text shows up properly throughout the system.

Lookin at this instructions: http://developer.android.com/guide/topics/ui/notifiers/notifications.html#CustomExpandedView If you set up your background color for the LinearLayout container then you can have your colors in notification for text and the background.

If the default colour for the notification text is defined by the launcher application then you cannot retrieve it from the android defaults settings unless the launcher is sharring this information.

However, have you try to remove this line android:textColor="#000" from your layout so that it can get automatically the default color?

The solution is to use built-in styles. The style you need is called TextAppearance.StatusBar.EventContent in Android 2.3 and Android 4.x. In Android 5.x material notifications use several other styles: TextAppearance.Material.Notification, TextAppearance.Material.Notification.Title, and TextAppearance.Material.Notification.Line2. Just set the appropriate text appearance for the text view, and you will get the necessary colors.

If you are interested how I have arrived at this solution, here's my trail of breadcrumbs. The code excerpts are taken from Android 2.3.

  1. When you use Notification and set the text by using built-in means, the following line creates the layout:

    RemoteViews contentView = new RemoteViews(context.getPackageName(),
    com.android.internal.R.layout.status_bar_latest_event_content);
    
  2. The mentioned layout contains the following View which is responsible for viewing notification text:

    <TextView android:id="@+id/text"
    android:textAppearance="@style/TextAppearance.StatusBar.EventContent"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:singleLine="true"
    android:ellipsize="marquee"
    android:fadingEdge="horizontal"
    android:paddingLeft="4dp"
    />
    
  3. So the conclusion is that the needed style is TextAppearance.StatusBar.EventContent, which definition looks like this:

    <style name="TextAppearance.StatusBar.EventContent">
    <item name="android:textColor">#ff6b6b6b</item>
    </style>
    

    You should note here that this style doesn't actually reference any of the built-in colors, so the safest way is to apply this style instead of some built-in color.

One more thing: before Android 2.3 (API Level 9), there were neither styles, nor colors, there were only hard-coded values. If you happen to have to support such old versions for some reason, see the answer by Gaks .

Solution by Malcolm works fine with API>=9. Here's the solution for older API:

The trick is to create the standard notification object and then traverse the default contentView created by Notification.setLatestEventInfo(...). When you find the right TextView, just get the tv.getTextColors().getDefaultColor().

Here's the code that extracts the default text color and text size (in scaled density pixels - sp).

private Integer notification_text_color = null;
private float notification_text_size = 11;
private final String COLOR_SEARCH_RECURSE_TIP = "SOME_SAMPLE_TEXT";


private boolean recurseGroup(ViewGroup gp)
{
final int count = gp.getChildCount();
for (int i = 0; i < count; ++i)
{
if (gp.getChildAt(i) instanceof TextView)
{
final TextView text = (TextView) gp.getChildAt(i);
final String szText = text.getText().toString();
if (COLOR_SEARCH_RECURSE_TIP.equals(szText))
{
notification_text_color = text.getTextColors().getDefaultColor();
notification_text_size = text.getTextSize();
DisplayMetrics metrics = new DisplayMetrics();
WindowManager systemWM = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
systemWM.getDefaultDisplay().getMetrics(metrics);
notification_text_size /= metrics.scaledDensity;
return true;
}
}
else if (gp.getChildAt(i) instanceof ViewGroup)
return recurseGroup((ViewGroup) gp.getChildAt(i));
}
return false;
}


private void extractColors()
{
if (notification_text_color != null)
return;


try
{
Notification ntf = new Notification();
ntf.setLatestEventInfo(this, COLOR_SEARCH_RECURSE_TIP, "Utest", null);
LinearLayout group = new LinearLayout(this);
ViewGroup event = (ViewGroup) ntf.contentView.apply(this, group);
recurseGroup(event);
group.removeAllViews();
}
catch (Exception e)
{
notification_text_color = android.R.color.black;
}
}

Call extractColors ie. in onCreate() of your service. Then when you're creating the custom notification, the color and text size you want are in notification_text_color and notification_text_size:

Notification notification = new Notification();
RemoteViews notification_view = new RemoteViews(getPackageName(), R.layout.notification);
notification_view.setTextColor(R.id.label, notification_text_color);
notification_view.setFloat(R.id.label, "setTextSize", notification_text_size);

For 2.3+ (From Android documentation):

Use the style android:TextAppearance.StatusBar.EventContent.Title for the primary text.

Use the style android:TextAppearance.StatusBar.EventContent for the secondary text.

For 2.2-, do what Gaks suggested in another answer to this thread.

If you want to compile against 2.2 and support 2.3+, and support all the variety of devices out there, Gaks' solution is the only one I know.

BTW, what Google suggested about using the value ?android:attr/textColorPrimary for 2.2-, isn't working. Just try it using the emulator. Gaks' way is the only way.

More resources: This and this do not work.

Here is solution for any SDK version using only resources.

res/values/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationTitle">
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textStyle">bold</item>
</style>
<style name="NotificationText">
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
</style>
</resources>

res/values-v9/styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="NotificationText" parent="android:TextAppearance.StatusBar.EventContent" />
<style name="NotificationTitle" parent="android:TextAppearance.StatusBar.EventContent.Title" />
</resources>

res/layout/my_notification.xml

...
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="title"
style="@style/NotificationTitle"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="text"
style="@style/NotificationText"
/>
...

P.S: Hard coded values are used for 2.2-. So problems can occur with some rare old custom firmwares.

I've found a very simple solution directly changing the name of the attribute provided by Android.

As you can see in this tutorial: http://www.framentos.com/android-tutorial/2012/02/20/how-to-create-a-custom-notification-on-android/

You only need to use a different attribute:

<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>

Hope this can help you!

Solution from @Malckom didn't help me at Lolipop with dark notification background because of TextAppearance.Material.Notification.Title is a system hardcoded color. Solution from @grzaks did, but with some changes within notification creating process:

NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setContentTitle(NOTIFICATION_TITLE_TIP)
.setContentText(NOTIFICATION_TEXT_TIP);
Notification ntf = mBuilder.build();
// ...
if (NOTIFICATION_TEXT_TIP.equals(szText)) {
notification_text_color = text.getTextColors().getDefaultColor();
} else {
if (NOTIFICATION_TITLE_TIP.equals(szText)) {
notification_title_color = text.getTextColors().getDefaultColor();
}
}
// ...

I know it is an old question but it could help someone else ; ) I do this in my app and that works perfect in some few lines :

    RemoteViews notificationView = new RemoteViews(context.getPackageName(), R.layout.notification_layout);


if (SDK >= LOLLIPOP) {


TextView textView = new TextView(context);
textView.setTextAppearance(context, android.R.style.TextAppearance_Material_Notification_Title);


notificationView.setTextColor(R.id.title, textView.getCurrentTextColor());
notificationView.setFloat(R.id.title, "setTextSize", textView.getTextSize());


textView.setTextAppearance(context,android.R.style.TextAppearance_Material_Notification_Line2);


notificationView.setTextColor(R.id.contentText,textView.getCurrentTextColor());
notificationView.setFloat(R.id.contentText,"setTextSize",textView.getTextSize());


textView = null;


}

I use this on the TextView in question:

style="@style/TextAppearance.Compat.Notification.Title"

It gives me white text if the background is black and black text if the background is white. And it works at least as far back as API 19.

Here is what worked for me to resolve this error. Add the following to styles.xml

    <style name="TextAppearance">
</style>
<style name="TextAppearance.StatusBar">
</style>
<style name="TextAppearance.StatusBar.EventContent">
<item name="android:textColor">#ff6b6b6b</item>
</style>
<style name="TextAppearance.StatusBar.EventContent.Info">
<item name="android:textColor">#ff6b6b6b</item>
</style>

In your expanable or collaspse layout set android:background as a "@android:color/transparent" because this is automatically take your device notification theme color and set as a background

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="180dp"
android:layout_margin="0dp"
android:background="@android:color/transparent"
android:orientation="vertical">
<TextView


android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorSecondary"
android:textStyle="bold" />

And your textview color define black and white as in Resource/color file in simple color file and as well as night mode color file