Tuesday, November 20, 2012

Why Do Motorola's ListViews Look Different

There was a helpful article "Why Does My ListView Look Different?" that Motorola posted a few years ago.  Unfortunately it has disappeared from the internet, as you might tell by clicking on the link.  It had some crucial information on fixing a weirdness for Motorola devices that I wanted to preserve for posterity (in case anyone else starts looking for it).

Essentially, when you're on some Motorola devices, the bottom of the ListView has a shadow on it by default.  In addition to that, if the ListView is supposed to take up the entire screen (even if it's not full), it doesn't.  Here's a screenshot of the bug in action:


The problem is android:overScrollFooter.  Motorola has a default one set and it causes sadness.  To get rid of it, set android:overScrollFooter="@null" in your ListView.  For bonus points, set it in a style and set that as your theme's default ListView so you never have to deal with this problem again.

Monday, November 12, 2012

The Unknown Style: actionBarWidgetTheme

One of the cooler features I've found in the Android Action Bar is grouped menu items.  If you group together a series of menu items in a submenu, you get a pretty popup of those items.  For example, if you set a submenu with android:checkableBehavior="single" then it'll have a radio button next to each item in the popup menu:



I wanted to reskin the RadioButton so that they all looked Holo, even on versions using ActionBarSherlock.  Naturally, I turned to my theme and set the radioButtonStyle.  However, the code below did not work:
<style name="MyTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
    <item name="android:radioButtonStyle">@style/MyRadioButtonStyle</item>
</style>

<style name="MyRadioButtonStyle" parent="@android:style/Widget.CompoundButton.RadioButton">
    <item name="android:button">@drawable/my_radio_button</item>
</style>

I was fairly lost for a few hours because radioButtonStyle was clearly working on everything except for the widget popup.  I could insert RadioButtons into my Activity and they'd pick up the new style, but the old style would remain for the action bar.  What was going on?

The culprit is android:actionBarWidgetTheme.

Introduced in API 14, what it does is let you style Views inflated by the action bar separately from the rest of your Application or Activity.  This is a neat trick, but if you don't know it exists, you could easily get lost on why your styles aren't applying to action bar Views.

If android:actionBarWidgetTheme is undefined, it falls back to the current theme's values.  For most Android themes, that means you're fine.  But in the case of Theme.Holo.Light.DarkActionBar, it does set the actionBarWidgetTheme to "@android:style/Theme.Holo".  As a result, it will not fallback to your theme's default value.

I solved the problem by setting my own actionBarWidgetTheme that is a sub-style of the original actionBarWidgetTheme:

<style name="MyTheme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
    <item name="android:actionBarWidgetTheme">@style/MyActionBarWidgetTheme</item>
</style>

<style name="MyActionBarWidgetTheme" parent="@android:style/Theme.Holo">
    <item name="android:radioButtonStyle">@style/MyRadioButtonStyle</item>
</style>

It's important to properly parent the actionBarWidgetTheme with whatever the theme was previously setting as its parent; otherwise you may miss out on some styling elsewhere.

Friday, November 9, 2012

Styling an AutoCompleteTextView

I ran into some confusion recently styling some AutoCompleteTextViews.  An AutoCompleteTextView is a compound View - it's got both an EditText component and a floating dropdown component.  The former is rather straightforward to style, by the dropdown is difficult because it's a mixture of attributes on the AutoCompleteTextView itself and styles set in the theme via android:dropDownListViewStyle.

For example, if you want to setup a custom background on the dropdown, you do it in the AutoCompleteTextView:

<AutoCompleteTextView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:popupBackground="@drawable/bg_autocomplete" />

But if you want to change the dividers, you have to create a theme and point that to a style, which isn't an immediately obvious solution:

<style name="MyTheme">
  <item name="android:dropDownListViewStyle">@style/DropDownListViewStyle</item>
</style>

<style name="DropDownListViewStyle">
  <item name="android:divider">#4F4F4F</item>
  <item name="android:dividerHeight">1dp</item>
</style>

The worst is when attributes in the AutoCompleteTextView and normal ListView styles collide.  I made the mistake of assuming that since it adopts a ListView-like style, it would adopt all ListView attributes - like android:listSelector.  The below, however, does not work:

<style name="DropDownListViewStyle">
  <item name="android:listSelector">@drawable/list_selector</item>
</style>

Instead, AutoCompleteTextView has its own attribute, android:dropDownSelector.  You have to set it like this:

<AutoCompleteTextView
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:dropDownSelector="@drawable/list_selector" />