Wednesday, February 1, 2012

Android Tablet project lessons learned

A little while ago I had the opportunity to work on a tablet-only app, running Android 3.1 or higher.
The app consists of only one screen, on which everything can be done.Therefore the app basically consists of one Activity and several Fragments (about 4 to be precise).
The Activity controls the Fragments for potential re-use of the Fragments, as recommended. As a basis the official Honeycomb Gallery project was used. Quite unique to this app was though that it has to have 3 columns instead of the usual 2...

To get a feeling for the app, here are some screenshots:


The very first time the user has to log in, after which a sync with the server is done. Note the progress bars showing in the background.


Synchronisation in progress... Notice that the title is indented for some reason. Needs some investigation why it shows here, and not in the Login DialogFragment...


After the data has been loaded, the user can filter all data by selecting from the dropdowns, and selecting items in the lists. The rightmost column then shows a list of questions, dependent on the selected filters. Many types of questions and actions had to be supported; beside the ones you see, also built were dropdowns, multiple radiobuttons, view images and PDFs, etc.

Lessons learned

  • Prevent EditText in a ListView from losing focus/text disappearing:

    android:windowSoftInputMode="adjustPan"



  • Sometimes Eclipse/DDMS loses the connection with the emulator, it just can't "see it" anymore. For example when you want to run an app in the emulator, the start dialog does not show your running emulator. To fix that run 'adb kill-server', that restarts the server. After this the emulator should be detected again.

  • You can't disable a RadioGroup such that it disables it contained radiobuttons. You have to disable each radiobutton within that radiogroup seperately.

  • Note that all Activities in your app might have had onDestroy() called, but the Application instance might still be around! So watch this when you have static variables, like a reference to the database... it might still be open when your Activity's onCreate() is called again!

  • The 4.0 emulator was taking ages to start up (gave up after about 30mins). Then I compared the settings of my 3.1 AVD with the new 4.0 AVD. It turns out that the hardware property 'Abstract LCD density' setting of the 4.0 AVD is set by default to 240, which means high density. That implies a lot of pixels to draw. Read somewhere that that's one of the issues of a slow starting AVD. So I changed that property to be the same as of the 3.1 AVD, so made it 160. After that, the 4.0 AVD started about as fast as the 3.1 AVD (several minutes).
    I also reduced the 'Device ram size' setting from 512 to 256, but don't think that was the one that fixed it. See also here.

  • The monkeyrunner recorder script records really slow on a 160 density Android 3.1 AVD. Playback is a lot faster. Here's a bunch of instructions, including 2 scripts for recording/playback which you can also find here and here.

  • Had a problem trying to show the progress indicator in a ListFragment with the correct top margin. Tried adding addHeaderView() on the fragment to show some text above a fragment list. That works fine, except: when showing the progress indicator, it does not take into account any (top margin) LayoutParams being set! The app needs to set that to stay below the Action Bar which is in "overlay" mode. As soon as the adapter is set, the margin is taken into account correctly. I found the following here: in onCreateView() there is also a similar problem, they set the layoutparams explicitly again:


    // For some reason, if we omit this, NoSaveStateFrameLayout thinks we are
    // FILL_PARENT / WRAP_CONTENT, making the progress bar stick to the top of the activity.
    ... code where LayoutParams are set ...

    Tried several combinations of setting the LayoutParams, but those didn't work either. As a workaround now I have added a FrameLayout above the <fragment> in the main Activity xml. It is a <fragment> with a FrameLayout with a LinearLayout with some TextViews in it. For that one the top margin is set, and since the ListFragment is below it, it is pushed down correctly, even when the progress indicator is shown. Maybe the FrameLayout is causing the problems? Maybe a custom list fragment element would fix it.

  • How to open a PDF from an app's local private storage:


    // Assuming you have stored a file like this (note the mode):
    FileOutputStream f = context.openFileOutput("theFilenameYouStoredThePdfAs", Context.MODE_WORLD_READABLE);

    ... write bytes to f, do other stuff...

    // Then you can get a reference for it for an external app via:
    File file = activity.getFileStreamPath("theFilenameYouStoredThePdfAs");
    if (file.exists()) {
    Toast.makeText(activity, activity.getString(R.string.openingFile), Toast.LENGTH_SHORT).show();
    Uri path = Uri.fromFile(file);
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(path, "application/pdf");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    activity.startActivity(intent);
    }


  • Definitely take a look at the latest Google I/O app, that's always a good reference for figuring out how to do specific things; I consider it a best-practices app (I've also seen it being referred to in blog posts/presentations from the Android team, can't find them now :).

No comments: