Thursday, May 5, 2011

Landing Activity Icons The Easy Way

Many Android applications include a landing activity or a "main page" full of icon options. Typically this activity does little more than throw intents to launch other Activities when a user clicks one of the icons. In this post, I'm going to take advantage of the onClick, tag, and drawableTop attributes of TextView to make this work with very little code.

 Let's imagine that I'd like to write a media playing application that includes main activity icons for Music, Photos, and Video. For each icon on the landing activity, I'm going to use just a tiny bit of xml in my layout:


First, you'll notice that the text and drawableTop attributes specify the drawable and the text for the icon. Using drawableTop keeps you from having to define a linear layout for each icon to stack the icon on the text. Here you can do it with just the TextView element and that's all.

Second, notice the style attribute. Here's the style I've defined for a main icon:

<style name="mainicon">
<item name="android:layout_width">100dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:gravity">center</item>
<item name="android:clickable">true</item>
<item name="android:onClick">click</item>

The obvious bits of style specify the height, width, and gravity. What isn't so obvious is the definition of clickable and onClick. Basically, the onClick attribute is telling every View that has this style to execute the method called "click" in the parent Activity. But wait, hows that going to work? We're telling every icon to run exactly the same code. Here's the code for the click event method:

public void click(View view) {
   String action = (String) view.getTag();
   Intent intent = new Intent(action);

The click method pulls the tag from the view that was clicked and fires an Intent using that string as the action. Take a look again at the XML that defines our music icon. See the tag attribute?

Now, there's one final step to make this work. The AndroidMainifest.xml must be modified so that the music Activity knows how to receive the intent we're throwing. Easy:

<activity android:name=".Music">
<action android:name="com.example.action.MUSIC"/>
<category android:name="android.intent.category.DEFAULT"/>

The intent filter will route all of the action.MUSIC intents to the Music activity. Hopefully this will simplify your code and clean things up a bit Have fun.

Sunday, May 1, 2011

Deep Linking in Android Applications

I'd like to write a little bit about best practice ideas for loading data in Android applications. I'm hoping to encourage you to rethink how you visualize an Android application. In general I'm going to advocate the use of implicit intents and loaders. Here's my main point of the post:

Good Android applications can be started from any Activity.

Here's the issue. A lot of mobile programmers come from a history of designing applications that load view controllers in a sequence. iOS and WinMo applications typically launch by firing up a specific controller. Many developers will make this a splash page or a welcome screen of some sort. From there, users navigate through the app, but the developer is always sure that the code in the starting controller has executed. It often becomes a sort of dumping ground for "init" code.

 Android isn't designed this way. You should think of your Android application as a mesh of Activities like a web site is a mesh of pages. You can't (and shouldn't) control how your user enters your application. Consider for example. A sizable amount of traffic to the site does not come in through the home page. Many people post links to specific product pages and the design encourages traffic to enter any place in the site. Effectively there is no front door.

 Good applications on Android are the same. They all offer deep-linking through the Intent mechanism. In this post, I'm not going to describe how intents work. I'll assume you know the basics. Most app developers know how to start an activity using an explicit intent:

startActivity(new Intent(context, MovieActivity.class));

Note that the explicit intent includes a class reference to the Activity you're launching. While this gets the job done, it doesn't open any doors for other apps to deep link into yours. Instead you should use an implicit intent such as the following:

startActivity(new Intent("app://com.example.movies/viewmovie/5"));

 Using an implicit intent enables other applications to launch this activity directly. However, if your application isn't designed correctly this may cause problems. What if you haven't loaded the data you need for the activity? Let's imagine that you have a master-detail arrangement where activity A shows a list and activity B shows the detail for a list item. In a poor application design, activity A loads the data for all items and passes the information to activity B.


In a better design, activity A loads only the data required to display the list. It should pass only an id to activity B which will load the detail page on it's own.


Each activity should have it's own Loader. The Android 3.0 SDK made the idea of loaders official, but the principle of each activity loading it's own data is solid even in older versions of the SDK.