Home > android, ui > AlertDialog, custom ListView items, and Honeycomb

AlertDialog, custom ListView items, and Honeycomb

Here is another compatibility tip for Honeycomb and previous versions. This one has to with custom ListView item layouts inside alert dialogs.

Ok, so I have a few places in my app that display an AlertDialog with a ListView inside, backed up by a adapter that provides custom item layouts. Getting the text color inside those layouts is a little tricky.

Prior to Honeycomb, dialogs always use a dark background and light text, regardless of which theme (light or dark) is used by their parent activity. This is true for AlertDialogs and plain Dialogs. However, lists inside AlertDialogs use an inverted (light) background. This is usually enabled automatically, or you can enable it yourself by calling AlertDialog.setInverseBackgroundForced. Now TextViews inside item views returned by your adapter can use black text, or, preferably, just in case the color is customized by the device manufacturer, a theme attribute reference (one of textAppearanceMediumInverse, or textColorPrimaryInverse, etc.).

Here is an alert dialog with a list view, running on the Sony Ericsson Xperia Arc. The background is not quite white, it’s a bit greyish (purplish?), and the text is pure black.

The item layout looks like this (only text views are shown):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/pick_attachment_root"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<TextView
		android:textAppearance="?android:attr/textAppearanceMediumInverse"
		android:id="@+id/pick_attachment_main_title"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_toRightOf="@id/pick_attachment_icon"
		android:layout_toLeftOf="@id/pick_attachment_toggle"
		android:singleLine="true"
		android:ellipsize="middle"
		android:textStyle="bold" />

	<TextView
		android:textAppearance="?android:attr/textAppearanceInverse"
		android:id="@+id/pick_attachment_sub_title"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_toRightOf="@id/pick_attachment_icon"
		android:layout_toLeftOf="@id/pick_attachment_toggle"
		android:layout_below="@id/pick_attachment_main_title" />

</RelativeLayout>

On Honeycomb and later, if you use the native, Holographic, themes, dialogs can be light or dark, matching the parent activity’s theme. AlertDialogs with lists look the same as regular dialogs with text, so this is what the above layout looks like on an Sony Tablet S running Android 3.2:

The text views turned invisible, white on white. How did this happen? The dialog’s background is no longer inverted, as the dialog’s theme matches the parent activity’s, but our text appearance references still assume inverted background: for a light parent theme, it’s “whatever text color is appropriate over the inverse of a light background”, in other words, “whatever is appropriate for a black background”, which is white. Switching the parent activity to a dark theme produces black text on black background by the same logic.

Ok, so for Honecomb, we’re supposed to use non-inverted theme attribute references. How to do this without duplicating our layouts inside res/layout-v11? My solution is this:

First, define these styles inside res/values-v4/list_styles.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

	<!-- Text for listboxes, inverted for Andorid prior to 3.0 -->

	<style name="MyListTextAppearanceSmall">
		<item name="android:textAppearance">?android:attr/textAppearanceSmallInverse</item>
	</style>

	<style name="MyListTextAppearanceDefault">
		<item name="android:textAppearance">?android:attr/textAppearanceInverse</item>
	</style>

	<style name="MyListTextAppearanceMedium">
		<item name="android:textAppearance">?android:attr/textAppearanceMediumInverse</item>
	</style>

</resources>

Then use these styles inside the layouts returned by the adapter backing up the listview inside the AlertDialog (… that Jack built…). The layouts themselves don’t need to be duplicated, mine are in the “basic” res/layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:id="@+id/pick_attachment_root"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content">

	<TextView
		style="@style/MyListTextAppearanceMedium"
		android:id="@+id/pick_attachment_main_title"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_toRightOf="@id/pick_attachment_icon"
		android:layout_toLeftOf="@id/pick_attachment_toggle"
		android:singleLine="true"
		android:ellipsize="middle"
		android:textStyle="bold" />

	<TextView
		style="@style/MyListTextAppearanceDefault"
		android:id="@+id/pick_attachment_sub_title"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:layout_toRightOf="@id/pick_attachment_icon"
		android:layout_toLeftOf="@id/pick_attachment_toggle"
		android:layout_below="@id/pick_attachment_main_title" />

</RelativeLayout>

Now all that’s left to do is to redefine these three styles inside res/values-v11/list_styles.xml and to use regular, not inverted, text appearance references:

<?xml version="1.0" encoding="utf-8"?>
<resources>

	<!-- Text for listboxes, non-inverted starting with Android 3.0 -->
	
	<style name="MyListTextAppearanceSmall">
		<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
	</style>
	
	<style name="MyListTextAppearanceDefault">
		<item name="android:textAppearance">?android:attr/textAppearance</item>
	</style>

	<style name="MyListTextAppearanceMedium">
		<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
	</style>

</resources>

The same dialog now looks like this:

And when the parent activity uses a theme dervied from @android:style/Theme.Holo, the dialog items will turn black, with light text.

About these ads
Categories: android, ui
  1. Santiago
    January 10, 2014 at 6:43 pm

    The setInverseBackgroundForced method call is what I need …. GENIUS!

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 103 other followers