Archive for the ‘Android’ Category

Simplifying Interaction with Contacts

With the introduction of the new ContactsContract to replace the previous Contacts API in Android 2.0, came a drastic increase in both functional capability and API complexity for applications wishing to interact with the Contacts Provider.  This level of access allows applications to make very detailed and specific additions and updates to the Contacts/People database on the device; however it can make some of the common simpler operations seem like they require an unnecessary amount of code.

We are going to look at some ways to try to simplify some common interactions that an application may need to have with the Contacts Provider through the use of a few Intents that ContactsContract provides.  In this article, we are going to use Intents to let the existing device Contacts UI handle the following operations, simply returning a result to our application where appropriate:

  • Selecting a contact
  • Adding a new contact
  • Searching for an existing contact to edit

The ContactsContract API certainly provides all the functionality necessary for you to build your own UI to manage the data your application is interested in by way of making queries, updates, and inserts on the URIs for each table in the Content Provider.  However, in many cases the system-defined UI for these tasks is not only adequate, but helps provide a consistent experience to the user.

Selecting (Picking) A Contact

This operation involves displaying a list of the the users contacts so they can select one to perform further actions on (perhaps to send that person and email or call them).  Rather than creating your own picker UI, you can use the following code to create an Intent and launch the Contacts application’s existing picker:

Intent intent = new Intent(Intent.ACTION_PICK);
intent.setData(ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT);

Since we have launched the new Activity expecting a result, onActivityResult() will be called when the user is finished, and the data Uri will contain a lookup URI that can be used to inspect the details of the Contact selected by the user:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if(requestCode == PICK_CONTACT && resultCode == RESULT_OK) {
    //returns a lookup URI to the contact just selected
    Uri selectedContact = data.getData();
  }
}

Adding A New Contact

This operation involves inserting a new contact on the user’s device.  Due to the large number of tables involved in storing all the common kinds of data (phone, email, address, etc.), constructing this insert using the raw provider queries can be quite daunting.  ContactsContracts provides a series of actions and extras in the ContactsContract.Intents.Insert utility class.

Intent intent = new Intent(Intent.ACTION_INSERT);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);

// Just two examples of information you can send to pre-fill data
intent.putExtra(ContactsContract.Intents.Insert.NAME, "Dave Smith");
intent.putExtra(ContactsContract.Intents.Insert.COMPANY, "Xcellent Creations, Inc.");
startActivityForResult(intent, ADD_CONTACT);

There is a host of extras defined in ContactsContract.Intents.Insert that can be used to pre-fill data in the add form, but here is a list of some more common choices:

  • ContactsContract.Intents.Insert.NAME
    • Full name of the new contact
  • ContactsContract.Intents.Insert.COMPANY
    • Company/Organization of the new contact
  • ContactsContract.Intents.Insert.EMAIL
    • Email address of the new contact
  • ContactsContract.Intents.Insert.PHONE
    • Phone number of the new contact
  • ContactsContract.Intents.Insert.POSTAL
    • Address for the new contact

As with the picker, launching the new Activity with a result expectation will call onActivityResult() with a lookup Uri for the newly created Contact record, so that your application can display the details of the insert if you wish.

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if(requestCode == ADD_CONTACT && resultCode == RESULT_OK) {
    //returns a lookup URI to the contact just inserted
    Uri newContact = data.getData();
  }
}

Search For A Contact

Finally, the ContactContract API includes another convenience intent to look for a specific contact record by telephone number or email address.  The Intent action ContactsContract.Intents.SHOW_OR_CREATE_CONTACT can be passed with a mailto: or tel: scheme Uri, and it will search for a matching contact.  If a match is found, the contacts details are displayed for further review or editing (all within the standard Contacts application UI).  If no match is found, by default a dialog will be displayed asking if the user would like to create a contact with the given email or phone.

Intent intent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT);
//PICK ONE of the following:
//For searching via telephone number
// intent.setData(Uri.fromParts("tel", "8001234567", null));
//For searching via email address
// intent.setData(Uri.fromParts("mailto", "johndoe@gmail.com", null));

//We'll use an email search for the example
intent.setData(Uri.fromParts("mailto", "johndoe@gmail.com", null));

startActivity(intent);

The same extra parameters may be passed along with the Intent to pre-fill the entry form in case the matched contact is edited or if the user opts to create a new contact.  In addition, a special extra is defined (ContactsContract.Intents.EXTRA_FORCE_CREATE) that allows you to skip over the user dialog and immediately open the create contact form if no matches are found.  Here is an example that includes some fields to be pre-filled and skips the dialog for creating a new contact:

Intent intent = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT);
intent.setData(Uri.fromParts("tel", "8001234567", null));

//If no match and we create, skip the user prompt dialog
intent.putExtra(ContactsContract.Intents.EXTRA_FORCE_CREATE, true);

//Values to tag along for an insert
intent.putExtra(ContactsContract.Intents.Insert.NAME, "David Smith");
intent.putExtra(ContactsContract.Intents.Insert.COMPANY, "Xcellent Creations, Inc.");

startActivity(intent);

Notice in this case that we aren’t interested in any result returned by the Contacts application. With the SHOW_OR_CREATE_CONTACT Intent, no interesting data is returned from the operation.

Hopefully these tips will help take the complexity out of using the Contacts API for those cases where your application does not need to utilize the full access of the Content Provider!

A Developer’s Adventures in Rooting

When I started developing applications for Android in early 2009, I never expected that I would ever own a rooted device.  I felt that, as a developer, I needed to be testing my applications in an environment that best emulated my users, and rooting would compromise that environment.  However, recently I have been forced to take a second look at that opinion in light of some unfortunate barriers Android developers face today.  Let me also go on record to say that these are made unfortunate because I believe that they should not exist at all, and would not exist with Google and device OEM support.  This is the story of my journey to solve a major problem, and how rooting was the only viable solution.

The Problem

When I set out on this journey, I did so because there were a number of vital debugging tasks that I simply could not do on any physical device; and I was tired of resorting to the emulator just to perform them.  As a goal, the primary tasks I wanted on my devices were:

  • Using DDMS File Explorer to view my application’s internal storage (the data directory where files and databases created by an application are stored)
  • Using the HierarchyViewer tool to debug issues with layouts
  • Running ADB in TCP/IP mode (I will explain the importance of this one more later)

I didn’t feel like these demands were unreasonable, they are all a necessary part of developing applications.  When I look back, it’s a bit of a shock to me that this is even a problem.  My first test phone was the ADP1; the first developer phone that could be purchased directly from Google by registered Android Market developers.  This phone set a great precedent because the device was completely unlocked and was running a userdebug build of Android; meaning that all the things I listed above were possible on the device without even a second thought.

Nowadays, the “developer phone” is a thing of the past, and you can no longer select and purchase phones already set up for userdebug in the Android Market portal.  Today, if you want to develop applications on a device, you must purchase a commercial device from a retail location.  This means that you can not perform any of these useful debugging functions directly on a device…where they really count.

Google instead now provides support via instructions for building userdebug images of the AOSP from source for their latest devices (the Nexus One and Nexus S at the time of this writing).  This seems helpful on the surface, except most application developers do not have the full AOSP build environment set up on their systems.  In the days of the ADP1, these images were at least available on the HTC dev site built and ready to be flashed!  But I digress…

The Journey Begins…

Just to establish context, I currently have the following devices that I use for developing applications:

  • HTC ADP1
  • Original Motorola Droid
  • HTC EVO 4G
  • Samsung Nexus S 4G
  • Motorola Droid 2

None of these devices, except the ADP1, are set up to allow proper access to use the debugging tools I wanted.  I decided that, since these issues were primarily due to lack of permissions on the device (like the inability for ADB to run in root mode), perhaps these problems could be solved simply by rooting one of my test devices.

Step 1: Root

I chose my OG Droid as the device to use.  It had been around the longest, so there should be plenty of information out in the community about rooting it.  Plus, if something happened to it in the process I wouldn’t be super sad.  I used a program for the PC name SuperOneClick to root the device and install SuperUser, an Android application that helps manage root access to installed applications.  I didn’t realize it at the time, but SuperOneClick also installed BusyBox on the system.  I’ll explain a bit later why this was actually a bad thing.

With my device rooted, I went to test the success of my efforts by attempting to run ADB over WiFi on my device…success!  I could now run the adb tcpip <portnum> command and subsequently adb connect <ipaddress:portnum> from my development machine to debug applications without being tethered to the actual device.  In fact, with root access I could now even install one of the many applications available on Android Market that would enable/disable this service for me so I didn’t need to drop into the shell every time.

I was so excited I couldn’t wait to try out HierarchyViewer and File Explorer…but for some reason, these tools still did not work on the device.  I was confused as to what other piece of the puzzle could be required to get things up and running.

ADB WiFi

Besides the simple convenience of the matter, the primary reason that running ADB over WiFi was so important to me is because oftentimes I’m developing with Android in a context where the USB connector is not accessible.  The two main examples of that are when I’m developing with an accessory device connected (like the ADK) or when the Android device is encased or embedded in some larger system where the mechanical enclosure obstructs the USB connector.  In these cases, the prospect of debugging wirelessly makes the development process infinitely easier.

Step 2: Userdebug

It didn’t take much research to find out that the reason these tools no longer work is because the necessary features required to run them are explicitly disabled in production or “user” builds of the Android OS (this statement is made directly by Googlers from the Android Team all over the web).  Instead, an engineering (“eng”) or “userdebug” build is required to be installed on the device.  One of the primary functions needed was the ability to run the ADB daemon as root (try typing <adb root> with your device plugged in and you’ll see what I mean).

This meant I needed to install a custom system ROM image.  Luckily, my device had already been rooted, which is the first step needed to install said custom image.  However, finding the image to load would prove a bit more difficult.  For most production phones, a stock userdebug image doesn’t seem to exist, although if you own one of the Nexus phones it seems you can undertake the task of building one yourself from source (see the link at the beginning of this post).  I found the answer in the modding community via Cyanogenmod.  While I don’t believe most of the popular custom ROMs out there (Cyanogenmod, MIUI, etc.) are technically built as userdebug (the ro.build.type property is still set to “user” on devices I’ve seen), they are built to allow the processes like ADB the full system access I needed it to have.  I used the very helpful ROM Manager application (available in Android Market) to handle the necessary steps of flashing a proper recovery image and installing Cyanogenmod.

The initial attempt to load the Cyanogenmod binary resulted in a phone that would not boot past the initial animation, but a second attempt (manually going into the recovery menu and applying the update again) yielded successful results.  So how was I faring with my laundry list now?  With Cyanogenmod installed, I was finally successful in attaching to HierarchyViewer on the device!  However, I was still unable to read anything other than the SD Card via File Explorer.

I was 2 for 3, but completely puzzled about what else I could even customize on the device to allow me proper access inside of DDMS.  I could drop into the shell on the command line and view all the files in all the directories on the device…so why couldn’t DDMS?

ViewServer

I wanted to make a quick note about the solution that came out of the Android team recently with regards to using HierarchyViewer on devices.  The ViewServer project, hosted by Romain Guy, is a version of the internal Android ViewServer that can be dropped into individual applications in order to enable the use of HierarchyViewer on devices.  I greatly appreciate the effort put forth to provide this to the community, but I still believe it is the wrong solution.  Developers should be able to configure their devices to freely use these tools globally throughout the system.

Step 3: Replacing System Binaries

After doing even more research, I came across an obscure set of posts on a few forums indicating that the system Toolbox was the culprit.  I mentioned earlier that SuperOneClick installed BusyBox on the device, which replaced the default system Toolbox for all primary commands.  Cyanogenmod also relies on BusyBox, so even if the rooting hadn’t done it, I have a feeling I would have ended up with BusyBox by this point anyway.

What Are All These Boxes?

If you’re unfamiliar with what all these “boxes” are, let me explain.  Linux systems typically have a collection of individual utility programs in the system directories that are invoked from the command line.  Commands like “ls”, “cat”, “ssh”, etc. are actually binaries that get executed to perform those specific tasks.  When Linux found its way into mobile and embedded systems, this collection of tools took up far too much space.  Enter BusyBox and Toolbox.  BusyBox and Toolbox are examples of single binary applications that implement a host of these common commands in a very tiny package, making it suitable for embedded systems.  In short, one of these two binaries is where you get your command line capability from in the Android shell.

The hidden problem that this created is that the BusyBox version of “ls” (which is what DDMS File Explorer uses) does not support all the functions DDMS expects to find (I believe the primary missing feature is file sorting) in the Toolbox version of “ls”.  So in order to solve this problem, I had to manually copy a good version of Android’s Toolbox back onto the device and re-link “ls” to point to it instead of BusyBox.  I obtained the correct copy of Toolbox from an emulator instance running the same base OS version and transferred the file to the system/bin directory of the device.  I then had to enter the shell on the device in order to remap the symbolic link for “ls” in the system/xbin directory to point to system/bin/toolbox.

Finally, I now could browse the device’s file system properly from within the DDMS File Explorer!  I now had a device worthy of being used for debugging!

The Plea

Needless to say, this process was an absolute pain; especially considering my position that the whole thing should not have been necessary.  Android developers should not have to be linux gurus or developing at the firmware level of the AOSP in order to have access to the full set of SDK debugging tools on their devices.  They key missing element is that developers need easy access to flash-ready system images that enable the userdebug features of the OS and devices that easily allow these images to be flashed.  Devices like the Nexus One, Nexus S, and some of the newer HTC devices that allow easy unlocking of the bootloader is a step in the right direction, but we need Google and the OEMs to step up and help provide the tools to finish the job.  Your community is counting on you!

Update: November 2011

As a comparison study, I spent some time going through the process of converting a “commercial” Nexus S 4G device (originally released via Sprint) to a userdebug build from the AOSP code. Comparatively, the process was about the same amount of effort, just spread out over a different set of tasks. The primary advantage here is that I was walking through a fairly well documented set of steps instead of researching each step one after the other…but the process still took a full day’s work to get initialized.

The primary piece of heavy lifting required to go through this process was to set up the build environment for the AOSP. If I can provide any advice here it is to build your environment inside of Ubuntu 10.04…and nothing else. I started initializing the environment inside of an Ubuntu 8.04 instance, and ran into multiple issues in setting up the build chain that ultimately pointed to required packages I could not get without an upgrade. I imagine trying to set up the environment in something even more different would result in even more headaches…hardly worth it if your doing this for a simplistic purpose such as this.

Once the environment is initialized, the non-AOSP proprietary drivers and binaries that each device requires must be unpacked and loaded into the code base manually before the image is built. Again, this process is well documented and there are scripts to extract each driver into the proper location…but it still must be done individually for each device you plan to build for. Beyond that, the process is simple and requires little interaction, even though there is a lot of waiting (syncing the source and building the target code). With about 6 command-line commands you have built and flashed the image onto the device.

If I had to recommend a method, I would choose this avenue just because it caused less frustration. However, you will be limited to the 3-4 devices at any one time that has current support in a branch of the AOSP source tree.

Synchronizing ScrollView

Recently, a post was made by Kirill Grouchnikov on his blog providing details on how the Android Market app uses a synchronized scrolling technique to take a certain view from inside the ScrollView content and let it float to the top of the screen when it would have otherwise been scrolled off-screen.  This post is meant to elaborate on his details with a splash of sample code.  Here are some screenshots of what we’re looking to accomplish:

          

Doing this hinges primarily on a simple custom subclass of ScrollView that tracks the onScrollChanged() method callback to keep all views in sync.  We could use this class to expose these scroll changes as a public interface, but in this example we attach the views of interest and do all the tracking inside the custom class.

package com.examples.synchronizedscrolling;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ScrollView;

public class SynchronizedScrollView extends ScrollView {

    private View mAnchorView;
    private View mSyncView;

    public SynchronizedScrollView(Context context) {
        super(context);
    }

    public SynchronizedScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public SynchronizedScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    /**
     * Attach the appropriate child view to monitor during scrolling
     * as the anchoring space for the floating view.  This view MUST
     * be an existing child.
     *
     * @param v View to manage as the anchoring space
     */
    public void setAnchorView(View v) {
        mAnchorView = v;
        syncViews();
    }

    /**
     * Attach the appropriate child view to managed during scrolling
     * as the floating view.  This view MUST be an existing child.
     *
     * @param v View to manage as the floating view
     */
    public void setSynchronizedView(View v) {
        mSyncView = v;
        syncViews();
    }

    //Position the views together
    private void syncViews() {
        if(mAnchorView == null || mSyncView == null) {
            return;
        }

        //Distance between the anchor view and the header view
        int distance = mAnchorView.getTop() - mSyncView.getTop();
        mSyncView.offsetTopAndBottom(distance);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //Calling this here attaches the views together if they were added
        // before layout finished
        syncViews();
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if(mAnchorView == null || mSyncView == null) {
            return;
        }

        //Distance between the anchor view and the scroll position
        int matchDistance = mAnchorView.getTop() - getScrollY();
        //Distance between scroll position and sync view
        int offset = getScrollY() - mSyncView.getTop();
        //Check if anchor is scrolled off screen
        if(matchDistance < 0) {
            mSyncView.offsetTopAndBottom(offset);
        } else {
            syncViews();
        }
    }
}

This example keeps track of two views, which must be laid out as children of the ScrollView (we’ll see an example layout in a minute).  We’ll call these views the anchor view and the synchronized view.  The synchronized view is the view that will float to the top of the screen when scrolled.  The anchor view is a view that lives inside the list of content and represents the space and location where the synchronized view should be if it weren’t floating.

The two workhorse methods in this example are syncViews() and onScrollChanged().  The job of syncViews() is to align the position of the floating view with its anchor.  We use this to align the views when everything is initialized, as well as when scrolling occurs and the anchor view is fully visible.

The onScrollChanged() callback is overridden to track the user’s scrolling and slide the view accordingly.  We calculate two important distance values with each call.  The first is the distance between the anchor view and the current top visible scroll position.  We use this value to determine if the anchor view is visible or has been scrolled off-screen.  If this value is negative, then the anchor view is at least partially scrolled off-screen, and we need to set the position of the synchronized view as an offset.  The second calculated value is the distance between the current visible scroll position and the current position of the synchronized view.  This is the value we apply as the offset to the synchronized view using offsetTopAndBottom().  As previously mentioned, when the anchor distance is zero or greater, the view is fully visible and the floating view should be aligned with the anchor’s position.

The Layout

The key to making this custom scrolling work is that the two views we are tracking must be laid out as children.  Here is the layout used in the example:

main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <com.examples.synchronizedscrolling.SynchronizedScrollView
    android:id="@+id/scroll"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <FrameLayout
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
      <LinearLayout
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <View
          android:layout_width="fill_parent"
          android:layout_height="65dip"
          android:background="#A00"/>
        <View
          android:id="@+id/anchor"
          android:layout_width="fill_parent"
          android:layout_height="65dip"
          android:background="#AAA"/>
        <View
          android:layout_width="fill_parent"
          android:layout_height="225dip"
          android:background="#0A0"/>
        <View
          android:layout_width="fill_parent"
          android:layout_height="225dip"
          android:background="#00A"/>
        <View
          android:layout_width="fill_parent"
          android:layout_height="225dip"
          android:background="#A00"/>
        <View
          android:layout_width="fill_parent"
          android:layout_height="225dip"
          android:background="#0A0"/>
      </LinearLayout>
      <TextView
        android:id="@+id/header"
        android:layout_width="fill_parent"
        android:layout_height="65dip"
        android:gravity="center"
        android:text="Heading View"
        android:background="#A555" />
    </FrameLayout>
  </com.examples.synchronizedscrolling.SynchronizedScrollView>
</FrameLayout>

Since ScrollView can only have one child, we use a FrameLayout to contain everything.  The traditionally scrollable list of items (here, represented as Views in assorted colors) are laid out in a vertical LinearLayout.  The View to be synchronized is laid out as a sibling of this list, but still contained in the FrameLayout

NOTE: The reason we do this instead of letting the LinearLayout be the sole child and let the floating view live outside the ScrollView completely has to do with scrollbars.  If all these views are not children of the ScrollView, then they will be drawn over the top of the scrollbars when they are visible.  This, as Kirill mentions in this post, is ugly.

Tie It Together

Finally, let’s take a look at the sample Activity that brings these pieces together to make the lovely screenshots we saw at the beginning of this article.

package com.examples.synchronizedscrolling;

import android.app.Activity;
import android.os.Bundle;

public class ScrollingActivity extends Activity {

    private SynchronizedScrollView mScrollView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mScrollView = (SynchronizedScrollView)findViewById(R.id.scroll);

        mScrollView.setAnchorView(findViewById(R.id.anchor));
        mScrollView.setSynchronizedView(findViewById(R.id.header));
    }
}

You can see there isn’t much to do here, since we do the heavy lifting inside our SynchronizedScrollView.  We simply need to attach the to views from our layout that represent the anchor and the floating view.

Multiple Clickable Zones in ListView Items

Developers often have a need to create rows in a ListView that have multiple interactive locations that the user can touch, instead of just one single clickable row.  This is a pattern that even Google has employed in apps like the DeskClock. DeskClock’s Alarm tab display each list item with a small toggle button inside, used to enable or disable each alarm.  In addition, the remainder of the list item is also touchable and takes the user to a screen to edit the alarm parameters.

                    

Taking a quick survey of questions and answers found on the internet, you may discover that many will say what I just described above is impossible.  The common method for adding a clickable item is to add a Button or ImageButton to a ListView row’s layout.  The side-effect of doing this is that the remainder of the list item is no longer selectable and this renders your OnItemClickListener useless!  But then, how is it that Google was able to accomplish this same task??  Luckily for us, Android is an open source project, so we can inspect what was done in DeskClock, and learn from it.

The Problem is Focus

As developers, we all have the same natural tendency to pick either a Button or ImageButton when we want to implement a clickable widget anywhere…including inside of ListView.  Sadly, this is where the downfall begins.  Button and ImageButton are not just subclasses of TextView and ImageView with their CLICKABLE flag enabled; they are subclasses with their CLICKABLE and FOCUSABLE flags enabled.  ListView, by design, does not pass perform click events on list items when those items contain FOCUSABLE views, regardless of how you configured any of its other flags (ListView actually protects the PerformClick method by first checking hasFocusable() on any list item).

Bottom line, in order to keep access to the default behavior of ListView, we need to use child views in our row layouts that are CLICKABLE without being FOCUSABLE.

Quick and Dirty

One option to circumvent this and still use typical buttons would be to create layouts that are nothing but Buttons, so that no area of the item layout exists that is not clickable.  This method, IMO, has one major drawback in that you completely lose the position tracking ListView provides you for any of the item clicks and you will have to manage that all yourself in the Button’s OnClickListener methods.  However, in some cases this method may be desirable and I would suggest that in those cases that you call setItemsCanFocus(true) on your ListView.  If you plan to fully obstruct the root layout with Buttons, this method will allow D-Pad and arrow focus changes to slide across all your buttons appropriately just like it was the list item itself.

Losing the Focus

So how do we implement clickable views that don’t also try to take focus?  Simple!  Any View object has the ability to be made clickable by calling setClickable(true) in Java or adding android:clickable=”true” in XML.  Consider replacing a Button with a TextView made clickable, or an ImageButton with an ImageView made clickable.  Make a composite button out of some widgets inside a LinearLayout and make the layout itself clickable.

NOTE: There is a shortcut here as well.  You may not be aware of the fact that, anytime you call setOnClickListener() on any View (even if the parameter is null), it will automatically set the clickable flag for the View for you.  Since, in most cases you are probably making something clickable so you can listen for the event, you don’t need to explicitly mark the flag on the views in your layout.

Here is an example of a layout that includes two ImageViews that we turn into clickable accessories by setting their clickable attribute:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="?android:attr/listPreferredItemHeight">
  <ImageView
    android:id="@+id/left"
    android:layout_width="?android:attr/listPreferredItemHeight"
    android:layout_height="fill_parent"
    android:src="@drawable/icon"
    android:scaleType="center"
    android:clickable="true"
    android:background="@drawable/mybutton" />
  <TextView
    android:id="@+id/text"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    android:gravity="center"
    android:textAppearance="?android:attr/textAppearanceLarge" />
  <ImageView
    android:id="@+id/right"
    android:layout_width="?android:attr/listPreferredItemHeight"
    android:layout_height="fill_parent"
    android:src="@drawable/icon"
    android:scaleType="center"
    android:clickable="true"
    android:background="@drawable/mybutton" />
</LinearLayout>

When this layout is used as the list item view, the item itself as well as each individual ImageView are tappable by the user.

          

There may still be cases where you want to use the traditional Button (to keep its styling, perhaps).  By removing the FOCUSABLE flag from the Buttons, they can be safely added to the row layout while keeping the list item itself interactive.  This can be accomplished by calling setFocusable(false) from Java or android:focusable=”false” in XML.  Here is an example of another list item layout that uses Buttons with their FOCUSABLE attribute removed:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="?android:attr/listPreferredItemHeight"
  android:gravity="center_vertical">
  <TextView
    android:id="@+id/text"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:textAppearance="?android:attr/textAppearanceLarge" />
  <Button
    android:id="@+id/left"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:text="Accessory1" />
  <Button
    android:id="@+id/right"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:focusable="false"
    android:text="Accessory2" />
</LinearLayout>

With both of these examples, the list item itself is still clickable and that event will register with the ListView’s OnItemClickListener.  The accessory views will need listeners attached to them to monitor for their click events.  Download the sample code attached to this post and you can see an example of attaching separate listeners to these views, including using tags to track the row position.

A Note About Drawables

You may have noticed in some of the screenshots of the examples above that the drawable states of the selectable buttons mirror that of the parent layout.  When the user presses the parent list item, the child buttons themselves are also highlighted.  Depending on your particular application, this may be an undesirable effect.  To find a solution to this problem, we turn again to the AOSP code for DeskClock.  The accessory in each list item does not highlight when the alarm time is pressed.  So how do we achieve this?

The key here is to define a custom widget that overrides the behavior of setPressed() to ignore the calls to set this state when they come from the parent layout.  Here is an example of a custom ImageView that we can use in our first example to handle this:

package com.examples.listzones;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;

public class NoParentPressImageView extends ImageView {

    public NoParentPressImageView(Context context) {
        this(context, null);
    }

    public NoParentPressImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void setPressed(boolean pressed) {
        // If the parent is pressed, do not set to pressed.
        if (pressed && ((View) getParent()).isPressed()) {
            return;
        }
        super.setPressed(pressed);
    }
}

We can now insert this custom widget into the row layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="horizontal"
  android:layout_width="fill_parent"
  android:layout_height="?android:attr/listPreferredItemHeight">
  <com.examples.listzones.NoParentPressImageView
    android:id="@+id/left"
    android:layout_width="?android:attr/listPreferredItemHeight"
    android:layout_height="fill_parent"
    android:src="@drawable/icon"
    android:scaleType="center"
    android:clickable="true"
    android:background="@drawable/mybutton" />
  <TextView
    android:id="@+id/text"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_weight="1"
    android:gravity="center"
    android:textAppearance="?android:attr/textAppearanceLarge" />
  <com.examples.listzones.NoParentPressImageView
    android:id="@+id/right"
    android:layout_width="?android:attr/listPreferredItemHeight"
    android:layout_height="fill_parent"
    android:src="@drawable/icon"
    android:scaleType="center"
    android:clickable="true"
    android:background="@drawable/mybutton" />
</LinearLayout>

Now, the child views will only display their pressed states when actually pressed. They will no longer mirror the state of their parent.

          

Please feel free to download the sample code from this link to take a closer look at how all of this works together!

Quick Rounded Corners

Application developers often want to display images with rounded corners.  A common technique for accomplishing this in Android is to construct a new mutable Bitmap object, draw a rounded rectangle, and then draw in the image contents using transfer modes.  An example of a method that would accomplish this is below:

public Bitmap createRoundedCornerBitmap(float radius, Bitmap src) {
    //Create a *mutable* location, and a canvas to draw into it
    int width = src.getWidth();
    int height = src.getHeight();
    Bitmap result = Bitmap.createBitmap(width, height, Config.ARGB_8888);
    Canvas canvas = new Canvas(result);
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);

    RectF rect = new RectF(0, 0, width, height);
    paint.setColor(Color.BLACK);
    canvas.drawRoundRect(rect, radius, radius, paint);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
    canvas.drawBitmap(src, 0, 0, paint);
    paint.setXfermode(null);

    return result;
}

This method is born out of a more generic way of applying solid mask shapes to images.  It just so happens that, in this case, the mask we are applying is a rounded rectangle shape.  However, this method does have one primary drawback in a lot of applications in that you must post-process each image to create another before displaying them to the user.  Perhaps there is a simpler way to do this that only affects the presentation layer?

Enter ClipPath

It turns out, we can add just a few lines of code to the way ImageView draws its contents to obtain the same behavior.  Below is a simple custom subclass of ImageView that applies this technique.

package com.examples;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.ImageView;

public class RoundedImageView extends ImageView {

    public RoundedImageView(Context context) {
        super(context);
    }

    public RoundedImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        float radius = 25.0f;

        Path clipPath = new Path();
        RectF rect = new RectF(0, 0, this.getWidth(), this.getHeight());
        clipPath.addRoundRect(rect, radius, radius, Path.Direction.CW);
        canvas.clipPath(clipPath);

        super.onDraw(canvas);
    }
}

Notice here that we apply a simple rounded rectangle Path as the clip region on the canvas before allowing ImageView to continue to draw the image contents.  The clip of a Canvas can be thought of as the specific region where you want to allow drawing to occur.

Using A Custom Widget

Because this method involves reconfiguring how ImageView draws the image to the screen, it pretty much requires that you create a subclass.  This may worry some who are not used to working with custom widgets and views in Android.  Basically, any View you want to place in your layout that does not exist in android.view or android.widget must be full referenced in the XML.  So, if you build your layouts in XML, then all you need to change is the tag name, replacing it with the fully-qualified version representing the new widget.  For example, a traditional ImageView such as

<ImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:scaleType="centerCrop"
  android:src="@drawable/placeholder" />

Doesn’t need a package name because it lives in android.widget.  It can be replaced by our new rounded corners version by changing the XML to

<com.examples.RoundedImageView
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:scaleType="centerCrop"
  android:src="@drawable/placeholder" />

Now we have a reusable widget that draws images with rounded corners without needing to manipulate the underlying image data.  I would urge you to experiment further with both of these techniques to create more creative masks and clips on your views.

Manufacturing Unique R.id Values

If you have been developing in Android for any length of time, you’ll most likely be aware that one of the most useful portions the resource framework is the fact that Views and other resources can be given an android:id tag in their XML declaration, and Android will make sure to compile all the ids into unique integer values that can be accessed from Java code by calling R.id.. This allows us to reduce the number of event listeners an application requires because it can uniquely identify the view that triggered the event by matching its id.  And, of course, we prefer using integer ids for this as opposed to matching Objects or String tags because the ids fit nicely into switch statements!

For example, a layout with two buttons, both pointing at the same action method:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <Button
    android:id="@+id/firstButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Click Me!"
    android:onClick="onButtonClick"
  />
  <Button
    android:id="@+id/secondButton"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="Click Me Too!"
    android:onClick="onButtonClick"
  />
</LinearLayout>

We can determine which button was pressed in the Activity by checking the sender’s id:

public class MyActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    public void onButtonClick(View v) {
        switch(v.getId()) {
        case R.id.firstButton:
            //Handle first button click
            break;
        case R.id.secondButton:
            //Handle second button click
            break;
        default:
            break;
        }
    }
}

This is not breaking new, and is just one of many useful properties of android:id…
But what if we didn’t use XML to create our layouts/menus/etc.?  What if we created a series of Views in Java code?  Are we destined to go wanting without the assistance of the unique R.id generator?  Certainly not!

Making id Elements Yourself

To reserve a set of ids for your own use in application, simply create an ids.xml file in the res/values directory of your project.  The syntax coupled with creating this file is pretty self-explanatory

res/values/ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <item type="id" name="firstButton"/>
  <item type="id" name="secondButton"/>
  <item type="id" name="thirdButton"/>
</resources>

Now let’s take these fresh ids and add them to a series Buttons created in a dynamic layout:

public class MyActivity extends Activity implements View.OnClickListener {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout layout = new LinearLayout(this);
        layout.setOrientation(LinearLayout.VERTICAL);

        Button button;
        button = new Button(this);
        button.setOnClickListener(this);
        button.setText("Click Me First!");
        button.setId(R.id.firstButton);
        layout.addView(button);

        button = new Button(this);
        button.setOnClickListener(this);
        button.setText("Click Me Next!");
        button.setId(R.id.secondButton);
        layout.addView(button);

        button = new Button(this);
        button.setOnClickListener(this);
        button.setText("Then Click Me!");
        button.setId(R.id.thirdButton);
        layout.addView(button);

        setContentView(layout);
    }

    public void onClick(View v) {
        switch(v.getId()) {
        case R.id.firstButton:
            //Handle first button click
            break;
        case R.id.secondButton:
            //Handle second button click
            break;
        case R.id.thirdButton:
            //Handle third button click
            break;
        default:
            break;
        }
    }
}

That’s it!  Just another practical application of a little known resource type in the Android framework.

The Missing Manual: Android Bluetooth RFCOMM

Bluetooth communication has been a part of the Android SDK since the 2.0 release late last year. The APIs available in the android.bluetooth package are primarily focused around three application functions:

  1. Discovering other devices
  2. Connecting to devices
  3. Transferring data using the RFCOMM layer

With this capability, developers can write applications that transfer data between devices using peer-to-peer connections, or even communicate with other Bluetooth enabled electronics.  The SDK provides a very good sample project geared towards illustrating these basic functions in the context of a chat program.  But with all the assistance the SDK documentation provides, there are a few important pieces that are missing from the big picture of working in the RFCOMM connection stack.

A Little Bluetooth Background

Bluetooth is a very flexible wireless standard enabling devices in close proximity to discover, connect, and transfer information across miniature peer-to-peer networks.  As part of the discovery and connection process, the Bluetooth stack relies on a protocol called Service Discovery (SDP) to gather information about the devices it is discovering to determine whether they have the right capabilities to warrant connecting with.  As part of SDP, devices must publish a UUID for each service they have available.

A typical discover/connect operation would have the following steps:

  1. Scan for discoverable devices in range
  2. Use SDP on a specific device to find the UUID of the service you want to connect with
  3. Connect to the specific device referencing your preferred service

RFCOMM is a layer of the Bluetooth Protocol Stack that provides serial data transfer services (via “sockets”).  It is the base of many of the common profiles such as Dialup Networking (DUN) and Serial Port Profile (SPP).  In essence, though, it is a way for developers to create a full duplex serial data stream between two points.

And Now…Back to Android

As previously mentioned, in order to establish a connection to a remote device using Bluetooth, you must be able to discover and reference a specific service record on that device.  This gives rise to two possible paths by which this can be done:

  1. Know the UUID of the remote service ahead of time
  2. Read the SDP responses to determine the UUID fo what you need

In the sample program provided in the SDK, the former option is used.  Because the application is a point to point link between two Android devices, and the sample works for both sides of the conversation, the UUID is hard-coded into the application.  This method does work, and in many cases is the proper way to handle things.  But what if the device your connecting to isn’t under your control, like an embedded sensor or media device?  Isn’t this why SDP exists in the first place?

It turns out, there are some holes in the current SDK API and documentation that make discovering unknown services a little difficult, but there are solutions.

Discovery via a Hidden Function

The Android SDK does include two methods in the BluetoothDevice class:

  • public boolean fetchUuidsWithSdp()
  • public ParcelUuid[] getUuids()

Both of these methods are designed to poke the underlying service and get references to the service records.  And in the current SDK (2.2 as of this writing) they are both hidden.  Niether of these methods can be called directly, even though they are in the code.  They have to be called using reflection, and there is no guarantee that they will stay the same in future version of the SDK since they are not public.

BluetoothDevice.getUuids() is a synchronous method, and will return an array of the UUID records to the caller directly.  BluetoothDevice.fetchUuidsWithSdp() is an asynchronous method, and you will have to register for the Broadcast Intent that the system fires for the available UUID that is found.

  String action = "android.bleutooth.device.action.UUID";
  IntentFilter filter = new IntentFilter(action);
  registerReceiver(mReceiver, filter);

Notice the misspelling of the word “bleutooth”, which is not a typo; that is the true Intent action string.  These Intents will include two extras that you can use to get the data you need

  • android.bluetooth.device.extra.DEVICE
    • Contains the BluetoothDevice that is being queried.
  • android.bluetooth.device.extra.UUID
    • Contains an array of ParcelUuid objects for the services.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
  @Override
  public void onReceive(Context context, Intent intent) {
    BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
    Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");
    //Parse the UUIDs and get the one you are interested in
  }
};

UUID via Failed Connect

The only currently exposed in the SDK that also calls fetchUuidsWithSdp() under the hood is BluetoothSocket.connect().  By creating and attempting to connect to a BluetoothSocket, you will get the same UUID Broadcast Intent, at which point you could read it, find the services you need, and then connect again.  This is a very counterintuitive method since a BluetoothSocket requires a service’s UUID to be created!

Closing Remarks

Hopefully, Android will clear up their documentation and expose the proper functions in future releases.  But for now, thanks to the visibility the AOSP gives us into the source code, we have a few workarounds to accomplish the goal of using Android to add rich connectivity into applications!

Adapting to Empty Views

Android has a pretty flexible framework for displaying data from a database, array, or other list structure using classes called Adapters.  There are plenty of great tutorials in the Android docs and throughout the web on how to use adapters to create custom behavior for display this data, so I’m going to focus on it’s less-discussed couterpart; the AdapterView.

Adapter Basics

Android uses an Adapter class to bind data from a data structure (like a database Cursor or an array) to a view that is specially designed to dynamically display the data as it moves and changes…this is the AdapterView.  There are many common subclasses of AdapterView (ListView, Spinner, Gallery, etc.), but they share the common function without any special overriding that I will discuss in detail today.

The Empty View

All AdapterView subclasses have a property that maintains a reference to a View object known as the empty view.  The point of this view is to be displayed when the Adapter bound to our data tells the AdapterView that the data structure has no data.  It does this either by just returning zero from getCount(), or through a more complex implementation of isEmpty().  Conceptually, it is then the role of the AdapterView to replace the view reserved for displaying content with this View, if it is not null.

Setting the Empty View for an AdapterView

You may call at any time AdapterView.setEmptyView(View v) on an AdapterView object to attach a view you would like to use as the empty view.  You may also call AdapterView.getEmptyView() at any time to verify if an empty view has been set.  That’s it…sort of.

There’s a trick…

As of this writing, one of the things the Android documentation doesn’t tell you is that the AdapterView simply manipulates the visibility properties of your content view and empty view based on the information it receives from the Adapter.  After determining which view to display, it simply calls setVisibility(View.VISIBLE) on that view, and calls setVisibility(View.GONE) on the other.  This is efficient and works just fine, but it requires that you create both views and insert them into a visible layout yourself.  AdapterView will not do this legwork for you.  You cannot assume that supplying an empty view to AdapterView means that it will insert that view in it’s place in the layout at runtime…

Example, please!

Let’s examine the basic ListView example to get a grip on things.  When you place a ListView in your layout per the SDK tutorials, it will look something like this:

File: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout1"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <ListView
    android:id="@+id/list1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:drawSelectorOnTop="false"
  />
  <TextView
    android:id="@+id/empty1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="Nothing in the List!"
  />
</LinearLayout>

and the setup code in your activity would be like this:

ListView list;
TextView empty;

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  empty = (TextView)findViewById(R.id.empty1);
  list = (ListView)findViewById(R.id.list1);

  list.setEmptyView(empty);

  //...code to create and set an adapter of your choice...
}

The key here is this works because both the ListView and it’s “empty view” are defined as children of the displayed root layout, so they are both laid out when the Activity is displayed.  Because of this, AdapterView’s method of simply manipulating visibility works perfectly.  Let’s modify the example now so that the layout XML looks like this:

File: main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/layout1"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <ListView
    android:id="@+id/list1"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:drawSelectorOnTop="false"
  />
</LinearLayout>
File: empty.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView
  android:id="@+id/empty1"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content"
  android:gravity="center_horizontal"
  android:text="Nothing in the List!"
/>

with the same setup code in our Activity.  Now, the layout process is not handling the setup and inflation of our “empty view”.  When our list is empty, AdapterView will manipulate the visibility of the two view objects…so the content view will become invisible.  However, with the empty view not attached to our layout, it will not show up anywhere on the screen (even though it has been set to View.VISIBLE)! In order for this second example to work properly, you would have to modify the Java code as follows:

ListView list;
TextView empty;

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);

  empty = (TextView)findViewById(R.id.empty1);
  list = (ListView)findViewById(R.id.list1);

  //New code to add the empty view to the visible layout
  LinearLayout rootLayout = (LinearLayout)findViewById(R.id.layout1);
  rootLayout.addView(empty, 0);

  list.setEmptyView(empty);

  //...code to create and set an adapter of your choice...
}

Take-home reminder: AdapterView does nothing more for you than manage visibility between a content view and an empty view. You are responsible to making sure those views are laid out in the appropriate locations prior to utilizing this functionality. You could also subclass the AdapterView you are using to handle it, but many AdapterView classes are not as straightforward as you might think. I would highly recommend reading the docs and the Android source thoroughly before doing so.

Looking Forward

Knowing this about AdapterView is actually a bit exciting because it means we have yet even more flexibility at our fingertips.  This means that we are not forced to have the AdapterView content and empty views occupy the same space.  We could define our content at the top of the screen (like an image gallery), and display the empty view somewhere else, or even on top of the entire screen (like a popup saying “nothing to see here, move along”) restricting access to other UI elements in the view.

9-Patches Explained

One of Android’s most well-known points to consider in application development is the requirement to support a variety of different screen sizes and orientations in an application. The SDK is full of documentation and tools on how to help the developer best accomplish this goal; one of these tools is the 9-Patch creator.

The 9-Patch is a PNG image with some coding added that allows the Android system to determine how the image can be stretched and contorted to meet the specific layout constraints during use (like fill_parent and wrap_content).  It does this by taking a predefined PNG image, and allowing the user to define a 1-pixel border around the image in locations where stretching can occur.

Creating a 9-Patch

  1. Navigate to the tools/ directory of your SDK from a command-line.
  2. Run draw9patch and a window will appear.
  3. Drag and drop and PNG image into the application window
    • The image will appear with a blank 1-px border around it
  4. Add stretchable regions by clicking on pixels in the newly created blank area (you will see black mark appear for each selection)
    • Remove any mistakes by holding the Shift key and clicking on a marked pixel
    • See below for more on this

9-Patch Theory

The 9-Patch gets its name from the fact that the overlay created breaks the image up into nine defined regions, organized similar to tic-tac-toe.  Each region has specific stretch properties:

  • Corner Regions (A,C,G,I)
    • These regions are fixed and nothing inside them will stretch
  • Horizontal Sides (D,F)
    • The pixels in these regions will stretch vertically when necessary
  • Vertical Sides (B,H)
    • The pixels in these regions will stretch horizontally when necessary
  • Center (E)
    • The pixels in this regions will stretch in both horizontal and vertical directions equally

Draw9Patch

The draw9patch tool allows the user to define two pieces of data by applying marks on the image border:

  • Stretchable zones
    • The portions of the image that are to be fixed or stretched when the image must fill or wrap in layout
    • Created by marking border pieces on the TOP and LEFT of the image
  • Content area
    • The area portion of the image where content is to be inserted (generally text or something applied via the android:src xml tag)
    • Created by marking border pieces on the BOTTOM and RIGHT of the image

As an example, below is what a basic patched graphic will look like inside the draw9patch tool.  The green regions define the patches that will stretch either horizontal or vertical.  The pink patch is a fully stretchable patch.

Original

Patched

You can see how the colored regions map to the example grid shown above.  This is a simple example, but the user can create multiple regions in the same image like so:

Now we have an image with multiple fixed and stretchable regions.  Again, pink will stretch in both directions, green in one direction, and the rest of the image will be fixed.

A Word About Content

Defining the content area gives the user the ability to assist Android in providing the proper location where source content (text, other images, etc.) are placed when this image is a background or other content wrapper image.  Think of the standard Button as an example.  When a user defines android:text (of android:src on an ImageButton), this is the button content.  That content is laid out inside of the content area that was defined in the button background image (a 9-patch, by the way).

With our example, if I define a solid mark across the bottom and right of the image, stopping just short of the rounded corners, a content area will be defined as shown in this preview pane:

Tricks of the Tool

  • Show Patches
    • Turn on the visual assistance of the green/pink patch locations
  • Show Content
    • In the preview pane, highlight the content section that exists in each orientation
  • Show Bad Patches
    • Allow draw9patch to evaluate whether you might be causing trouble by defining a stretchable area that includes components (like a piece of text or a gradient) that won’t always stretch consistently.  The “bad” patches are enclosed in bright red borders.

Android Layout Relationships

Developing UI layouts for Android is an experience unlike any other.  I have developed for both desktop and mobile on a variety of platforms, and the XML structure that Android uses is quite unique.  This can bring with it some issues and challenges as it runs contrary to the way many people think about designing their UI.  Today, I’m going to make some comments on questions that I see constantly regarding the XML keywords most commonly misused in creating layouts.

I’m going to focus on developing using XML, but the same rules apply when creating LayoutParams in code.

Me Or My Parent?

When laying out elements (or widgets) on the screen, it usually doesn’t take long for people to realize that there are two of many of the basic parameters associated with size and location.  Options like width, height, and gravity have parallel values named layout_width, layout_height, and layout_gravity; so what gives?  Parameters with the prefix layout_ are actually instructions that are to be passed to the PARENT of the object that they are placed on, while all other parameters are passed to the object itself.

This can be a source of confusion even when you want to do something as simple as center some text, so let’s look at an example.  Imagine we have a simple layout like the one listed below:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

We placed the constraint layout_gravity=”center_horizontal” so that the text would be centered on the screen, but if this layout gets rendered, the results are not as expected!  Why?  Well, in the example the TextView told the following to its parent LinearLayout:

  • Make me as wide as you are (layout_width=”fill_parent”)
  • Make me just tall enough to fit the text inside (layout_height=”wrap_content”)
  • Lay me out centered horizontally (layout_gravity=”center_horizontal”)

Points #1 and #3 are actually in direct opposition to each other.  The TextView has asked to be centered within the LinearLayout, but also be just as wide (so there is really no room to center it).  What we really wanted to do was to either:

  1. Tell the TextView ITSELF to center its own contents horizontally within the view bounds
  2. Tell the TextView to be JUST as wide as its text, and them center the whole view horizontally in the parent.

Example Fix #1

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

Example Fix #2

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:text="My Text Content"
  />
</LinearLayout>

Bottom Line: Remember that any parameter that starts with layout_ is actually an instruction for the parent view, and not the view where it is located!