Thursday, January 26, 2012

Another App Widget Compatibility Trick

I've written previously on the issue of preventing app widgets from appearing on particular versions of Android.  This is useful, for example, when you have a Honeycomb-style widget but support pre-Honeycomb devices.

I've got a second version of this trick which I think is a bit easier now.  Instead of preventing the system from seeing your AppWidgetProviderInfo, you can actually enable/disable the widget receiver itself based on configuration resource selectors.

First, setup your app widget receiver thus (important part bolded):
<receiver
    android:enabled="@bool/widget_enabled"
    android:name="com.company.appwidget.MyAppWidgetProvider" >
    <intent-filter >
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>

    <meta-data
        android:name="android.appwidget.provider"
        android:resource="@xml/appwidget_info" />
</receiver>

Then all you need to do is create a config.xml (in your /res/values/ directory) file that sets "widget_enabled":
<resources>
    <bool name="widget_enabled">true</bool>
</resources>

You can then leverage the resource qualifier system to enable or disable the widget as necessary for different configurations.  For example, if you wanted your widget enabled on all versions of Android except for v11 through v13, you could set it up like this:

/res/values/config.xml <-- widget_enabled=true
/res/values-v11/config.xml <-- widget_enabled=false
/res/values-v14/config.xml <-- widget_enabled=true

The main advantage this provides over my last solution is that you need only modify a bool; the other solution required you to create duplicate versions of the AppWidgetProviderInfo XML file (if you supported, say, v3 through v10 and v14+).

Tuesday, January 24, 2012

GridLayout Child View Clipping Issues

After back-porting GridLayout to 1.5+, I finally feel comfortable using it in my apps.  There's a bit of an initial learning curve, but I think that is mostly a function of un-learning the LinearLayout/RelativeLayout state of mind.  I've converted a number of my own layouts to use GridLayout and found them to be much faster/easier to understand afterwards.

There was one particular issue that I ran into that is worth noting, for its solution was not immediately obvious.  I was making a two-column GridLayout, where an image was on the left and text was on the right.  Here's a simplified version of this layout:
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <Space android:layout_width="100dp" />

    <TextView android:text="@string/two_cities" />

</GridLayout>

The two Views would align next to each other nicely, but the TextView on the right would end up overflowing off the edge (the example below is not cropped - this is how it appears on the device):



What was happening was that the TextView would take up the entire width of the GridLayout, instead of taking up just the width of the cell (as I expected).


It turns out the solution is to set the width of the TextView to zero, then let the View fill its space dynamically using layout_gravity:

<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <Space android:layout_width="100dp" />

    <TextView
        android:layout_width="0dip"
        android:layout_gravity="fill_horizontal"
        android:text="@string/two_cities" />

</GridLayout>

Now the problem is solved:



You need both of the highlighted attributes above; one or the other won't fix the problem.  However, it will nicely solve the issue and should be kept in mind anytime you setup a GridLayout child to fill horizontally or vertically.

Friday, January 13, 2012

Highlights from Android's New Design Docs

I've just finished reading through the wonderful new Android Design site Google just released.  A lot of the information is stuff you should already know if you're an Android developer.  However, I took notes of pages/information that I found particularly interesting (or new for ICS).  Listed in order of appearance on the site:

48dp Rhythm - Like me, you may have implicitly noticed this pattern before, but it's good to see it stated so explicitly.  (Same with the 8dp gap.)

Back vs. Up - Having been confused on the exact distinction for a while, this page is a breath of fresh air.

Action Bar Pattern - This entire page should be required reading for everyone.  It definitely looks like the Action Bar is the future, and this is the instruction manual.

Long Touch Multiselect - Breaking news: long touch no longer activates the contextual menu.  Now, it is purely for activating selection (aka multiselect).  Every longtime Android developer should be aware of this change.

(Personally, I didn't even notice this change despite owning a Galaxy Nexus for a month.  Perhaps that's because not even every Google app has switched to the new design yet.  Or perhaps it's because long touch is just not an intuitive way to design any functionality - I've long since given up putting any core functionality into long touch.  Still, it's worth noting the design shift.)

Android is not the iPhone - At least, that's what this section *should* be called.  This page will be most useful for showing to your designer or boss how Android differs from iPhone and why you shouldn't just port assets and design directly from one to the other.

Tuesday, January 10, 2012

GridLayout Library for Android 1.5+ Support

If you're like me, when you first heard about GridLayout, you were excited.  Then disappointed, because it's currently only available for Ice Cream Sandwich.  That is true no longer - I've taken the time to port a working version of it all the way back to Android 1.5 and above.

Check out the android-gridlayout library here.

The one caveat is that there is one method in GridLayout that I was not able to port back.  As a result, when you change the visibility of a child View of a compatibility GridLayout, you should also call GridLayout.notifyChildVisibilityChanged().

Let me know what you think, especially if there are any bugs.