Android Development – Fragments

In this post:

What is a fragment?

General Concepts

Creating a Fragment

Subclasses of fragments

Adding a User Interface

Adding a Fragment to an Activity

Adding a fragment without a UI

Managing Fragments

Performing Fragment Transactions

Communicating with the Activity

Creating event callbacks to the activity

Adding items to the Action Bar

Adding items to Context Menu

 

What is a fragment?

  • portion of user interface in an activity
  • modular section of an activity
    • that has it’s own lifecycle
    • receives its own input events
    • that you can add or remove from the activity while the activity is running
  • sort of like a “sub activity” that you can reuse in different activities

 

General concepts

  • Multiple fragments can be combined in a single activity to:
    • build a multi-pane UI
    • reuse a fragment in multiple activities

 

  • A fragment must always be embedded in an activity
  • It’s lifecycle is directly affected by the host activity’s lifecycle
    • when the activity is paused or destroyed, so are all the fragments in it
  • When the activity is running (in resumed lifecycle state) you can manipulate each fragment independently (e.g. add or remove them)
  • When you perform such a fragment transaction, you can also add it to a back stack that is managed by the activity

Back stack

The back stack allows user to reverse a fragment transaction (navigate backward) by pressing the Back button

 

  • Fragment lives in a ViewGroup inside the activity’s view hierarchy
  • defines its own view layout
  • can be inserted into the activity’s layout by declaring fragment in the activity’s layout file
    • as a <fragment> element
    • from your application code, by adding it to an existing ViewGroup

 

  • However, fragment is not required to be a part of the activity layout (UI screen), you may use a fragment as an invisible worker for the activity

 

Creating a Fragment

  • Create a sub class of Fragment
  • The Fragment class code looks a lot like Activity
    • callback methods similar to an activity – onCreate(), onStart(), onPause() ….
    • converting an existing android app to use fragments might simply be a moving of code from your activity’s callback methods into the respective callback methods of your fragment
  • Usually you should implement at least following lifecycle methods:
    • onCreate()
      • called when the fragment is being created
      • initialize here the essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed
    • onCreateView()
      • called when the fragment’s user interface is drawn for the first time
      • return a View from this method that is root of your fragments layout
      • or; return null if the fragment doesn’t provide a UI
    • onPause()
      • called (as the first indication) when the user is leaving the fragment
      • doesn’t always mean fragment is being destroyed
      • here you should persist the changes beyond the current user session (because the user might not come back
  • There are several other callback methods that you should implement to handle various stages of fragment lifecycle. Discussed in more detail in the section about Handling the Fragment Lifecycle

 

Subclasses of fragments

  • Few sub classes that you might want to extend instead of base Fragment class

 

Adding a User Interface

  • To provide a layout for a fragment, must implement the onCreateView() callback
    method

    • must return a View that is the root of your fragment’s layout
      • If your fragment is a subclass of ListFragment, the default implementation returns a ListView, so you don’t need to implement it
    • onCreateView() provides three parameters
      • a LayoutInflator object
        • to help inflate a layout
        • To return a layout, inflate it from a layout resource defined in XML
      • container parameter
        • is the parent ViewGroup (from the activity’s layout) in which your fragment layout will be inserted
      • savedInstanceState parameter
        • is a Bundle that provides data about the previous instance of the fragment, if the fragment
          is being resumed
    • The inflate() method takes three parameters
      • R.layout.example_fragment
        • The resource ID of the layout you want to inflate
      • container
        • The ViewGroup to be the parent of the inflated layout
        • important in order for the system to apply layout parameters to the root view of the inflated layout, specified by the parent view in which it’s going
      • false
        • A boolean indicating whether the inflated layout should be attached to the ViewGroup
          during inflation (False because system is already inserting the inflated layout into the container)

 

Snippet

public static class ExampleFragment extends Fragment {

   @Override

   public View
onCreateView
(LayoutInflater inflater, ViewGroup
container
,

                            Bundle
savedInstanceState
) {

       //
Inflate the layout for this fragment

       return
inflater
.inflate(R.layout.example_fragment,
container
, false);

   }

}

 

Adding a Fragment to an Activity

  • Two ways
    • 1) Declare the fragment inside the activity’s layout file
      • specify layout properties for a fragment as if it were a view
      • example: a layout for an activity with two fragments

<?xml version=“1.0”
encoding
=“utf-8”?>

<LinearLayout xmlns:android=http://schemas.android.com/apk/res/android&#8221;

   android:orientation=“horizontal”

   android:layout_width=“match_parent”

   android:layout_height=“match_parent”>

   <fragment android:name=“com.example.news.ArticleListFragment”

           android:id=“@+id/list”

           android:layout_weight=“1”

           android:layout_width=“0dp”

           android:layout_height=“match_parent” />

   <fragment android:name=“com.example.news.ArticleReaderFragment”

           android:id=“@+id/viewer”

           android:layout_weight=“2”

           android:layout_width=“0dp”

           android:layout_height=“match_parent” />

</LinearLayout>

      • android:name attribute specifies the Fragment class to instantiate in the layout
      • when system creates the activity layout
        • it instantiates each fragment specified in the layout
        • calls the onCreateView() method for each one, to retrieve each fragment’s layout
        • inserts the View returned by the fragment directly in place of <fragment> element
      • Each fragment requires a unique identifier
        • that the system can use to restore the fragment if the activity is restarted
        • that you can use to capture the fragment to perform transactions, such as remove it
        • Three ways to provide an ID for a fragment:
          • Provide the android:id attribute
          • Provide the android:tag attribute
          • If you provide neither of the two, the system uses the ID of the container view

 

    • 2) Programmatically add the fragment to an existing ViewGroup
      • You can add fragments to your activity layout any time while your activity is running
      • To make fragment transactions in your activity (add, remove, replace), you must use APIs from FragmentTransaction.
      • Get an instance of FragmentTransaction from you activity like this:

 

Snippet

FragmentManager fragmentManager = getFragmentManager();

FragmentTransaction
fragmentTransaction
= fragmentManager.beginTransaction();

 

      • Add a fragment using add() method.
        • Specify:
          • the ViewGroup in which to insert fragment (specify by resource ID
          • the fragment to add

 

Snippet

ExampleFragment fragment = new ExampleFragment();

fragmentTransaction.add(R.id.fragment_container, fragment);

fragmentTransaction.commit();

 

        • Must call commit() for the changes to take effect

 

Adding a fragment without a UI

  • You can add a fragment without a UI to provide a background behavior for the activity
  • To add a fragment without a UI
    • add the fragment from the activity using add(Fragment, string)
    • provide a unique string tag rather than a view ID
  • If the fragment doesn’t have a UI then the string tag is the only way to identify it using findFragmentByTag() method
  • This adds a fragment but doesn’t receive a call to onCreateView(), so don’t need to implement that method
  • FragmentRetainInstance.java sample in SDK (available through the SDK manager), located in your system as <sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

 

Managing Fragments

  • To manage fragments in your activity you need to use FragmentManager
  • call getFragmentManager() from your activity to get fragment manager
  • Things that you can do with FragmentManager include:
    • Get fragments that exist in the activity
      • with findFragmentById() – for fragments that have UI
      • findFragmentByTag() – for fragments that don’t have UI
    • Pop fragments off the back stack, with popBackStack() – simulating a Back command by user
    • Register a listener for changes to the back stack, with addOnBackStackChangedListener()
    • Open a fragment transaction that allows you to perform transactions such as add and remove fragments

            (For more information refer to FragmentManager class documentation)

 

Performing Fragment Transactions

  • Great feature about using fragments in your activity is the ability to add, remove, replace and perform other actions with them, in response to user interaction
  • Each set of changes that you commit to activity at the same time, is called a transaction (performed using APIs in FragmentTransaction)
  • Each transaction can be saved to a back stack managed by the activity, allowing user to navigate backward
  • Call commit() to apply the transaction to the activity
  • To add the transaction to a back stack you may call addToBackStack() before calling commit()

 

Snippet

//
Create new fragment and transaction

Fragment
newFragment
= new ExampleFragment();

FragmentTransaction
transaction
= getFragmentManager().beginTransaction();

 

//
Replace whatever is in the fragment_container view with this fragment,

//
and add the transaction to the back stack

transaction.replace(R.id.fragment_container,
newFragment
);

transaction.addToBackStack(null);

 

//
Commit the transaction

transaction.commit();

 

  • If you add multiple changes to the transaction [such as another add() or remove()] and call addToBackStack(), then
    • all the changes before you call commit() are added to the back stack as a single transaction
    • the Back button will reverse them all together
  • The order in which you add changes to a FragmentTransaction doesn’t matter, except:
    • You must call commit() last
    • The order in which you add fragments to the same container, determines the order they appear in the view hierarchy
  • If you do not call addToBackStack() when you perform a transaction that removes a fragment:
    • then that fragment is destroyed when the transaction is committed
    • the user cannot navigate back to it.
  • if you do call addToBackStack() when removing a fragment:
    • then the fragment is stopped
    • It will be resumed if the user navigates back.
  • Calling commit() does not perform the transaction immediately
    • (Rather) it schedules it to run on the activity’s UI thread (the “main” thread) as soon as the thread is able to do so
    • If necessary, however, you may call executePendingTransactions() from your UI thread to immediately execute transactions submitted by commit()
    • Doing so is usually not necessary unless the transaction is a dependency for jobs in other threads

 

Communicating with the Activity

  • The fragment can access the Activity instance with getActivity() and easily perform tasks on the activity, such as finding a view in the activity layout

 

Snippet

View listView = getActivity().findViewById(R.id.list);

 

  • Similarly, your activity can call methods in the fragment by seeking a reference to the Fragment
    from FragmentManager by ID or by tag

 

Snippet

ExampleFragment
fragment
= (ExampleFragment)
getFragmentManager
().findFragmentById(R.id.example_fragment);

 

Creating event callbacks to the activity

  • To share fragment events with the activity
    • define a callback interface inside the fragment; and
    • require that the host activity must implement it
  • When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary
  • Example:
    • A news application that has two fragments in an activity – one to show a list of articles (fragment A) and another to display an article (fragment B)
    • Fragment A must tell the activity when a list item is selected
    • Activity (upon listening the fragment A) can tell the fragment B to display the article
    • In this case, the OnArticleSelectedListener interface is declared inside fragment A

 

Snippet

public static class FragmentA extends ListFragment {

   

   //
Container Activity must implement this interface

   public interface OnArticleSelectedListener {

       public void
onArticleSelected
(Uri articleUri);

   }

   

}

 

    • The activity that hosts the fragment implements the OnArticleSelectedListener interface and overrides onArticleSelected() to notify fragment B of the event from fragment A
    • To ensure that host activity implements this interface, fragment A’s onAttach() callback method instantiates an instance of OnArticleSelectedListener by casting the Activity that is passed into onAttach()

 

Snippet

public static class FragmentA extends ListFragment {

   OnArticleSelectedListener
mListener
;

   

   @Override

   public void
onAttach
(Activity activity) {

       super.onAttach(activity);

       try {

           mListener
= (OnArticleSelectedListener)
activity
;

       } catch (ClassCastException e) {

           throw new ClassCastException(activity.toString() +
must implement OnArticleSelectedListener”
);

       }

   }

   

}

 

    • If the activity has not implemented the interface, then the fragment will throw a ClassCastException
    • If the activity has implemented the interface, the mListener will hold a reference to activity’s implementation of OnArticleSelectedListener, so that fragment A can share events with the activity by calling methods defined by the OnArticleSelectedListener interface
    • For example, if the fragment A is an extension of ListFragment, each time the user clicks a list item, the system calls onListItemClick() in the fragment which then calls mListener.onArticleSelected() to share the event with the activity

 

Snippet

 

public static class FragmentA extends ListFragment {

   OnArticleSelectedListener
mListener
;

   

   @Override    public void
onListItemClick
(ListView l, View v, int position, long id) {

       // Append the clicked item’s row ID with the content provider Uri

       Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);

       // Send the event and Uri to the host activity

       mListener.onArticleSelected(noteUri);

   }

   

}

 

    • The id parameter passed to onListItemClick() is the row ID of the clicked item, which the activity (or other fragment) uses to fetch the article from the application’s ContentProvider
    • More information about content provider is availbe in the Content Providers document

 

Adding items to the Action Bar

  • To add menu items from fragment to the activity’s Option Menu and the Action Bar:
    • call setHasOptionsMenu during fragment’s onCreate(), to indicate that the fragment would like to add items to the Option Menu – otherwise, it will not receive a call to onCreateOptionsMenu()
    • implement the onCreateOptionsMenu()
  • Items added to the Options Menu from the fragment are appended to the existing menu items
  • Fragment receives callbacks to onOptionsItemSelected() when a menu item is selected

 

Adding items to Context Menu

  • To register a view in your fragment layout to provide a context menu, call registerForContextMenu()
  • When user opens context menu, the fragment receives a call to onCreateContextMenu()
  • When user selects an item, the fragment receives call to onContextItemSelected()

 

Keep in mind that:

  • First, the host activity receives an on-item-selected callback for Context Menu or Options Menu menu items
  • If activity doesn’t handle the selected item, then the event is passed to the fragment’s callback


Reference

http://developer.android.com/guide/components/fragments.html

 

Videos

 

Advertisements
Tagged with: ,
Posted in Android Development

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: