October 31, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Building UIs with Android Fragments, Page 2

  • June 6, 2011
  • By Lauren Darcey & Shane Conder
  • Send Email »
  • More Articles »

Coding with Android Fragments

Although fragments simplify the coding of dynamic user interfaces, there is a small overhead cost to using them. Although your layouts will ultimately determine which fragments are displayed, the code will still need to make decisions to determine whether or not to update an existing fragment or launch a new activity, should that fragment not exist in the current activity context.

For instance, the following code uses the findFragmentById() method to determine if a fragment is available within a specific layout. If it is not defined, a new activity is launched to display that fragment.

@Override
public void onListItemSelected(int index) {
  SampleViewerFragment imageViewer = (SampleViewerFragment) getSupportFragmentManager()
      .findFragmentById(R.id.image_viewer_fragment);

if (imageViewer == null || ! imageViewer.isInLayout()) { Intent showImage = new Intent(getApplicationContext(), SampleViewerActivity.class); showImage.putExtra("index", index); startActivity(showImage); } else { imageViewer.update(index); } }

You also end up needing just as many activity classes as before, though not all will be used on every device. There is little reason not to design your applications with fragments, even if your current UI design or target devices don't require them at this time. In the future, or when supporting a wide variety of devices, most applications will likely find good use cases for fragments, although games using their own frameworks with all of the UI implemented through OpenGL are probably a reasonable exception.

Since user interaction now typically happens within the fragments themselves, you need to code up the logic to determine whether or not to launch an activity from with the fragment, itself, or from the parent activity. Either way, some communication is needed between the fragment and its activity. Any fragment can call the getActivity() method to determine the Activity class it exists within. From there, a call to the UI methods of the activity can enable the fragment to determine more about the screen and layout. Or, more simply, the fragment could just call a method within an Activity that does this checking on demand. We've gone this route by adding a callback via a listener object. The interface for the listener is defined in the custom fragment class and the method is implemented in the Activity class. Here's a complete implementation of a ListFragment class:

public class SampleListFragment extends ListFragment {
  private int index = 0;
  private ListItemSelectedListener selectedListener;

@Override public void onListItemClick(ListView l, View v, int position, long id) { index = position; selectedListener.onListItemSelected(position); }
@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setListAdapter(ArrayAdapter.createFromResource(getActivity(), R.array.image_titles, android.R.layout.simple_list_item_1));
if (savedInstanceState != null) { index = savedInstanceState.getInt("index", 0); selectedListener.onListItemSelected(index); } }
@Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("index", index); }
@Override public void onAttach(Activity activity) { super.onAttach(activity); try { selectedListener = (ListItemSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement ListItemSelectedListener in Activity"); } }
public interface ListItemSelectedListener { public void onListItemSelected(int index); } }

As you can see, this approach is straightforward. An adapter is set in the onActivityCreated() method. When the fragment is attached to an activity, the listener is assigned and verified by using the Activity instance passed in to the onAttach() method. When the user clicks on an item in the ListView, the listener is triggered by calling the onListItemSelected() that we've implemented in the Activity class. The state is saved and restored.

An interesting aspect here is the use of the savedInstanceState in the onActivityCreated() method. On the surface, this looks to just save which item is being viewed and restore that. When the orientation changes, the instance data is saved, and this section of code is triggered. If the orientation switched from the two fragment view to the single fragment view, the activity remains the same, which is the activity that will show just the list. But once this code is triggered, the intent that ultimately launches the viewer activity is triggered. And so, when you rotate from landscape to portrait (in the example) the viewer activity is shown. Pressing the back key returns the user to the list. Let's talk about the viewer activity.

We launch the viewer activity when the viewer fragment is not defined in the current activity. This activity must also know about the viewer fragment and any configuration it might need. As you see in the above code listing, this information is sent to the other activity using a parameter passed with the intent extras. This information is then passed on to the fragment in just the same way. Here's the complete code for a viewer activity to display the fragment on its own screen:

public class SampleViewerActivity extends FragmentActivity {
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { finish(); return; }
setContentView(R.layout.image_viewer_activity); Intent launchingIntent = getIntent(); int index = launchingIntent.getIntExtra("index", 0); SampleViewerFragment viewer = (SampleViewerFragment) getSupportFragmentManager() .findFragmentById(R.id.image_viewer_fragment); viewer.update(index); } }

As you see, it's merely a way to show the viewer fragment. One interesting aspect is that if its onCreate() method is called when the device has switched back to landscape mode, it finishes, returning the user to the previous screen. When it is only used on landscape screens, this method works fine. Should you choose to use it in other screen orientation configurations, this must be updated to match. For the sample, this creates a smooth transition between orientations.

The viewer fragment is very simple. It inflates a layout resource and returns a View object as a result of the onCreateView() method being called. Then it has an implementation of the update() method you've seen called a couple of times. That's all there is to it. You can view the code in the online code repository.

Now, when the user rotates from portrait mode to landscape mode, the screen switches from the single fragment per screen (activity) mode to the dual fragment mode. You can add further alternative layouts to better control the look on different types of screens. For instance, maybe you only want the two-fragment layout when on large screen tablets. Instead of putting the alternative layout resource in the /layout-land directory, you could put it in the /layout-xlarge-land directory. This is a case where you'd need to update the viewer activity to not finish merely when the screen orientation changes.

Here we see the landscape view:

Android viewer fragment - landscape

If you then change the orientation to landscape, you'll see the same image:

Android viewer fragment - change orientation

And pressing the back button will return you to the list view:

Android viewer fragment - list view

Conclusion

The Fragment API, introduced in Android 3.0, is a great API for creating flexible and dynamic user interfaces. You've learned how quickly a dynamic user interface can be created by walking through most of the pieces of an entire application that looks and behaves differently depending on screen orientation and screen size. Since this functionality is provided through a compatibility library, any applications that target Android 1.6 and beyond can benefit from the improvements to user interface flexibility. This has been a brief introduction to the Fragment API, which has many more interesting features, such as transitions, the back stack, dialog fragments, and more. When targeting Android 3.0 and later, there are even more features not found in the compatibility library.

About the Authors

Shane Conder Shane Conder and Lauren Darcey--Contributing Editors, Mobile Development--have coauthored two books on Android development: an in-depth programming book entitled Android Wireless Application Development (ISBN-13: 978-0-321-62709-4) and Sams Teach Yourself Android Application Development in 24 Hours (ISBN-13: 978-0-321-67335-0). When not writing, they spend their time developing mobile software at their company and providing consulting services.

 

 

Lauren Darcey

Tags: UI, Android

Originally published on http://www.developer.com.

Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel