Screen Orientation Change – Saving Focus

Handling screen orientation changes in Android is a pain.  However, if you want your application to present a professional user interface, you simply must handle them.  Imagine a scenario where the user starts to enter data in a text field on a G1.  After a bit of pecking on the virtual keyboard they decide to switch to the built-in keyboard to finish.  They switch to the keyboard only to find the cursor is now in a completely different field.  Very frustrating.  In this article we show you how to maintain the focus and cursor position during a screen orientation change.

Screen orientation changes are handled through the onSaveInstanceState and onRestoreInstanceState methods of an Activity.  The onCreate method is passed a Bundle representing the saved instance state.  If this Bundle is null then there is no state to restore.  Otherwise you must coordinate restoration between onCreate and onRestoreInstanceState.  Here we are only interested in the focus and cursor.  Below is a bit of code showing how to save these for an EditText field:

@Override
protected void onSaveInstanceState(Bundle outState)
{
   super.onSaveInstanceState(outState);

   View focusedChild = getCurrentFocus();

   if (focusedChild != null)
   {
      int focusID = focusedChild.getId();
      int cursorLoc = 0;

      if (focusedChild instanceof EditText)
      {
         cursorLoc = ((EditText) focusedChild).getSelectionStart();
      }

      outState.putInt("focusID", focusID);
      outState.putInt("cursorLoc", cursorLoc);
   }
}

As you can see from the code above, you do not always have a current focus.  In that case, nothing would be saved.  Otherwise we get the view ID and, if the view was an EditText, the cursor location.  These are then saved.

Now, let’s take a look at what happens during restore:

@Override
protected void onRestoreInstanceState(Bundle inState)
{
   super.onRestoreInstanceState(inState);

   int focusID = inState.getInt("focusID", View.NO_ID);

   View focusedChild = findViewById(focusID);
   if (focusedChild != null)
   {
      focusedChild.requestFocus();

      if (focusedChild instanceof EditText)
      {
         int cursorLoc = inState.getInt("cursorLoc", 0);
         ((EditText) focusedChild).setSelection(cursorLoc);
      }
   }
}

As you can see, we use a little trick here when restoring the focused view.  The focusID will default to View.NO_ID if it is not found.  This in turn will cause the findViewById to return null, and we will have nothing to restore.  Otherwise, we locate the view that had focus and restore it.  If this is an EditText view we also restore the cursor location.

So, there it is.  Hope you found this useful.

-OGB

One Response

  1. Hi.
    This works fine with an activity with some textboxes but Im trying to make it work with textboxes in a dialog.
    I get an id from a view somewhere but Im guessing its not in the dialog?
    The dialog is is a custom dialog.

    Any ideas on how to solve this?

    I also posted this question on stackoverflow (with some code to explain my case):
    http://stackoverflow.com/questions/9229859/regain-focus-to-views-in-dialog-after-orientation-changes