Archive for January, 2012

Adapting My Android Dashboard UI To Tablets

(Note: As of March 12, 2012, this article is obsolete.. For a much improved way to support tablets, see “My Dashboard User Interface on Android Tablets“.)

If you are looking for a quick way to adapt your Android mobile phone app to a tablet, this post might save you a lot of time. I was facing that challenge about a month ago as I was nearing the release of my first Android app. I came up with a solution that gets your app ready for a tablet in a couple of hours rather than a couple of days.

The solution is not perfect, but often pretty good is good enough. It buys you time to work on a better solution.

A Pretty Good Solution for the Dashboard UI

To illustrate how this works, I went back to the work I had done on the Dashboard user interface: How To Build A Dashboard UI In Android. I built a new version of this demo and made it work on tablets.

The Dashboard demo on a regular device is shown to the right. Below is what it looks like after converting to a tablet. I call this the “Dashboard XL” demo. It appears as a 400×600 window inside the large screen.

Prior to implementing my solution, the old Dashboard demo simply expanded to fill the tablet’s screen. It is easy to see why this is far from an acceptable solution. And that’s just a home screen. In a real app, there are many supporting layouts.

The rest of this blog article explains how this works and how it helped me meet my deadline for releasing an app that included support for tablets.

A Pretty Good Solution Explained

Basically what I did was take the existing set of layouts for my app and arrange for them to be used without modification and run inside a small frame on the large tablet screen.

Placing an existing layout in a black frame isn’t too hard to do. If you have a layout that already looks good, you can simply add two more layouts around it. I used two layouts so I could specify both a background color for the tablet screen and a background for the area holding the app activity.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:background="@color/large_background"
 android:layout_gravity="center"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <LinearLayout
 android:id="@+id/frame"
 android:orientation="vertical"
 android:background="@color/title_background"
 android:layout_width="400dip"
 android:layout_height="600dip"
 android:layout_gravity="center">
 </LinearLayout>
...
</LinearLayout>

Notice that I name the inner layout with the id “frame”. I did this so I could write a little bit of code to detect Large or XLarge configurations and have that code add an existing layout below the layout named “frame”. Also note that the second layout has a width of 400 and a height of 600. (More on that choice of dimensions below.)

The code that adds to the “frame” layout is shown below. This code could have been inside of onCreate, but instead note that it is a override of the Activity’s setContentView method.

@Override public void setContentView (int layoutId)
{
 Configuration c = getResources ().getConfiguration ();
 int size = c.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
 boolean isLarge = (size == Configuration.SCREENLAYOUT_SIZE_LARGE);
 boolean isXLarge = (size == Configuration.SCREENLAYOUT_SIZE_XLARGE);
 boolean addFrame = isLarge || isXLarge;
 int finalLayoutId = addFrame ? R.layout.large : layoutId;
 super.setContentView (finalLayoutId);
 if (addFrame) {
   LinearLayout frameView = (LinearLayout) findViewById (R.id.frame);
   if (frameView != null) {
     // If the frameView is there, inflate the layout given as an argument.
     // Attach it as a child to the frameView.
     LayoutInflater li = ((Activity) this).getLayoutInflater();
     View childView = li.inflate (layoutId, null);
     if (childView != null) {
       LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams
             (ViewGroup.LayoutParams.MATCH_PARENT,
              ViewGroup.LayoutParams.MATCH_PARENT,
              1.0F);
       frameView.addView (childView, lp);
     }
    }
}

This requires some explanation.

The method starts out with checking to see if we are running on a Large or XLarge configuration. If we are, it means that we want to end up with our smaller frame inside the large screen of the tablet. There is a line that sets up the finalLayoutId. It gets set to R.layout.large if we are adding the  frame. Otherwise, it is the original layout id, which comes in as an argument. Notice the call to “super.setContentView”. Whatever case it is, that is what gets the activity’s views on the screen.

If it is the case that we are adding to the “frame” layout, we use a LayoutInflater to create the layout we will add as a child below the frane layout. Then we add it with the call to addView.

The other thing that worked out well is that I needed only one implementation of setContentView. All of the Activity classes could use the same method definition. If you’ve read the previous article on the Dashboard UI, you may recall that all the activities share a common superclass: DashboardActivity.

So the setContentView method above is actually defined in DashboardActivity. All the classes use it automatically because each one has an onCreate method that looks like this one for HomeActivity. That saved a lot of time.

protected void onCreate(Bundle savedInstanceState)
{
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_home);
}

In summary, here are the changes I made:

  • Dashboard activity superclass adds a setContentView method.
  • setContentView checks to see if the device has a Large or XLarge screen.
  • If the config is for Large or XLarges, setContentView uses a different layout that has been designed specifically for L or XL.
  • setContentView then embeds the original layout in that one.

Large and XLarge screens. I had to do a bit of experimentation to figure out a reasonable default size for the layout that displays on the tablet screen. I found rough guidelines about screen size on the Android Developers’ website. They talked about the range of screens supported in Android. I worked out something based on those sizes.

  • XLarge screens are at least 960dp x 720dp.
    For that, I used a 400 x 600 layout for Dashboard-XL.
  • Large screens are at least 640dp x 480dp.
    Dashboard-XL uses 400 x 600 and 600 x 400 for landscape.
I knew what 400 x 600 looked like before I started. Since that fit pretty well, I went with that. The only one I am not completely satisfied with is landscape for large screens. The wider screen makes it a bit harder to fit everything in without scrolling.

A Pretty Good Solution for Gomoku League

The reason I had for coming up with this solution was to finish an app that I was working on. The app is called “Gomoku League”. It runs on phones and on tablets. It is a full app with about 15 different full-screen layouts. Its home screen looks like this on a Motorola Xoom tablet.
(Click to enlarge)
If you interested in trying something on a tablet that goes beyond this dashboard demo, download the free Gomoku League app.

Source Code

Source code for this demo app can downloaded from the following places: (1) download zip file from Google Docs; (2) download zip file from Wglxy.com. Sometimes people outside the United States have trouble downloading the file from Google Docs. If you have trouble, use the alternate download location or try a second time. Often, access to Google Docs is intermittent.

Be sure to do a clean build in Eclipse after you import the project. Use the Project – Clean menu item in Eclipse.

This demo app was constructed using Android 2.3.3 (API level 10).

A Longer Version of My Story

At the start of this note, I told you the short version of my story about my app. Here’s the longer version.

I just recently released an app in the Android Market. It was something I had spent a fair amount of time on, but most of that effort was toward Android running on phones. As I got closer to release, I started thinking more about what I should do about Android tablets. I knew from a few quick tests in the emulator and some feedback I got from Beta test users that my app was not looking that good on tablets. I was faced with a choice. Should I simply forget about tablets for the first release, or should I figure out how to fix the user interface to be compatible with the larger screen sizes?

I decided to support tablets, but then I was faced with the problem of how to do that. I had this feeling that doing it right was going to take awhile. After all, that has been my experience the entire 12+ months that I have been learning the Android platform. Something that I first thought would be easy turns out to take a bit of effort to learn — worth the effort in the long run. This time, however, I did not want to think about the long run. I wanted my app out quickly.

So I looked for a solution that would get me to the Android Market as quickly as possible.

Conclusion

I know this is not a perfect solution, but I was in a hurry. I wanted to get my app out without adding a lengthy delay while I learned about supporting tablets the right way. Overall, I think it is a pretty good solution. I’d love to hear from other people on this.

(One last note: This blog is called “More Is Not Always Better” for a reason. I thought it would be good to show that it’s more than just a catchy title.  Sometimes a less than perfect solution is just the thing to do — at least for awhile)

Gomoku League available in the Android Market


I finished and released my first Android app. The app is  Gomoku League. Your objective is not simply to win a game of Gomoku, it is to win enough games to be first in the league. It’s a free app so give it a try.

For more information on Gomoku League, see the full description in the Android Market: https://market.android.com/details?id=com.wglxy.gomoku.a.

Splash Screen Demo With Sound

I did a little experiment to see if I could add sound to my splash screen demo. I never had done anything with sound in Android but had wanted to try it out for quite some time. It turns out that it was very easy to do.

The demo app looks exactly like the previous Splash Screen demo. The only difference is that it nows plays sounds.  It plays two distinct sounds. The first is for the splash image. The second is for the main screen. The second sound plays when the main activity starts and at each time you reorient your Android device.

Here’s what I did:

1. I moved two mp3 and wav files into the res/raw folder.

2. In my activity’s onCreate method, I added code to create a new MediaPlayer objects. Then I started it playing. The code looks like this:

mp1 = new MediaPlayer (this, R.raw.laser_01);
mp1.start ();

3. In my activity’s onDestroy method, I added code to stop the media player and release any resources it had.

if (mp1 != null) {
   if (mp1.isPlaying ()) mp1.stop ();
   mp1.release ();
   mp1 = null;
}

If you are interested in the source code for the demo, here is a link to a download page for it: download Splash with Sounds.

In researching this, I came up with some questions that I want to follow up on.

  • When should you use SoundPool and when should you use MediaPlayer?
    From what I can tell so far, it seems like SoundPool is best for adding sound effects for apps. The MediaPlayer is better for longer sounds.
  • What’s the best way to maintain the state of a MediaPlayer?
    I had some trouble when I created the media player in the onCreate method and starting it onResume. I was getting an exception about the media player being in an illegal state. So I don’t yet understand the MediaPlayer methods fully.
  • How long is too long for a sound file being played in the main (UI) thread?
    The example I built plays the sound on the main thread. If the duration of the sound were longer, it would be good to move it into a background task.

References

Media Playback note – general information on the Android Developers website about the MediaPlayer

Question about sound on Stack Overflow

Android Sounds Tutorial by Lars Vogel - Example of a SoundPool. This seems like the way to go to have a set of sounds you use over and over again.



Follow

Get every new post delivered to your Inbox.

Join 73 other followers