Fragments - mgolokhov/dodroid GitHub Wiki
Let's talk some about Fragments. First question: What does it for? Fragments are used for making better UI on big screens devices. For example, tablets with diagonal more than 7-inches diagonal.
Next question you should ask. Why should i use fragments if i had Activity class already?
The point here is that fragment allows you to change a portion of Activity UI and make that change dynamically.
So you don't have to begin a new activity to show "results" of original activity. You could show information using original activity using part of the Activity UI.
How does it all work?
First of all fragment could not be used without activity. Fragment is embedded in an activity hosting it. You could think of fragment as a subactivity or mini-activity.
Fragment lifecycle is also attached to activity lifecycle it hosting. Let me show you fragment lifecycle from simple to more difficult.
It looks like the activity lifecycle.
To be simple fragment has extra callbacks which initialize and connects it to activity.
For example:
onCreate() -initialize essential components of the fragment.
[onCreateView()](http://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)) - creating fragments layout and return it to hosting activity layout.
Fragment is attached to activity so its lifecycle strictly depends on activity state .
If you waner research lifecycle relationship of activity and fragments more closely watch this daigram:
Next. Let's look at techniques we could use to place fragment's layout into activity layout. We could make it statically or dynamically.
Statically:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<fragment
android:id="@+id/titles"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="1"
class="course.examples.fragments.staticlayout.Fragment1" />
<fragment
android:id="@+id/details"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="2"
class="course.examples.fragments.staticlayout.Fragment2" />
</LinearLayout>
The class attribute in the fragment specifies the Fragment class to instantiate in the layout. When the system creates this activity layout, it instantiates each fragment specified in the layout and calls the onCreateView() method for each one, to retrieve each fragment's layout. The system inserts the View returned by the fragment directly in place of the element. Let's watch callbacks of fragments and hosting activity in case of static fragments:
D/myLogs: Fragment1 onAttach
D/myLogs: Fragment1 onCreate
D/myLogs: Fragment1 onCreateView
D/myLogs: Fragment2 onAttach
D/myLogs: Fragment2 onCreate
D/myLogs: Fragment2 onCreateView
D/myLogs: MainActivity onCreate
D/myLogs: Fragment1 onActivityCreated
D/myLogs: Fragment2 onActivityCreated
D/myLogs: MainActivity onStart
D/myLogs: Fragment1 onStart
D/myLogs: Fragment2 onStart
D/myLogs: MainActivity onResume
D/myLogs: Fragment1 onResume
D/myLogs: Fragment2 onResume
It shows us that at first Fragments are initialized. Each fragment return its piece of layout to hosting activity layout. Then after we got all fragment's layouts activity is started.
Dynamically:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:orientation="horizontal" >
<FrameLayout
android:id="@+id/fragment1_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/fragment2_container"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
At any time while your activity is running, you can add any fragment to your activity layout using ViewGroup. You simply need to specify one of the FrameLayouts declared in activity's layout in which to place the fragment. To add, remove, or replace a fragment you must use APIs from FragmentTransaction. You can get an instance of FragmentTransaction from your Activity like this:
//Get a reference to the FragmentManager
mFragmentManager = getFragmentManager();
//Start a new FragmentTransaction
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
You can then add a fragment using the add() method, specifying the fragment to add and the view in which to insert it.
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
Now let's monitor callback sequence of fragments and hosting activity in this case. I simply add then delete 2 fragments dynamically:
D/myLogs: MainActivity onCreate
D/myLogs: MainActivity onStart
D/myLogs: MainActivity onResume
D/myLogs: Fragment1 onCreate
D/myLogs: Fragment1 onActivityCreated
D/myLogs: Fragment1 onStart
D/myLogs: Fragment1 onResume
D/myLogs: Fragment2 onCreate
D/myLogs: Fragment2 onActivityCreated
D/myLogs: Fragment2 onStart
D/myLogs: Fragment2 onResume
D/myLogs: Fragment1 onPause
D/myLogs: Fragment1 onStop
D/myLogs: Fragment1 onDestroyView
D/myLogs: Fragment1 onDestroy
D/myLogs: Fragment1 onDetach
D/myLogs: Fragment2 onPause
D/myLogs: Fragment2 onStop
D/myLogs: Fragment2 onDestroyView
D/myLogs: Fragment2 onDestroy
D/myLogs: Fragment2 onDetach
Now let's discuss integration between fragments and activity that contains it.
First of all we could access the hosting Activity instance with getActivity() from current fragment and easily perform tasks such as find a view in the activity layout:
View listView = getActivity().findViewById(R.id.list);
Likewise you could call fragment methods by using FragmentManager() class:
ExampleFragment Fragment1 = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
Fragment1.GoParty();
What if we waner fragment share events with the hosting activity? How to make it better? And don't forget our fragments should be reusable in other activities.
First implementation you think is just to get hosting activity and call methods you need. But what about fragment's reusability? Using your fragment in other activities obliged you to change fragment's methods for each new hosting activity. Two Fragments should never communicate directly. So what can we do to make it all more positive?
We could use Interface to interact with hosting activity and other fragments in it. In some cases, you might need a fragment to share events with the activity. A good way to do that is to define a callback interface inside the fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the information with other fragments in the layout as necessary. Let's say we have two fragments TitlesFragment and QuotesFragment. Clicking on one of the titles in TitlesFragment we got Quote using QuotesFragment. And Let's say hosting activity is QuoteViewerActivity.
public class TitlesFragment extends ListFragment { private ListSelectionListener mListener = null; private static final String TAG = "TitlesFragment";
// Callback interface that allows this Fragment to notify the QuoteViewerActivity when
// user clicks on a List Item
public interface ListSelectionListener {
public void onListSelection(int index);
}
// Called when the user selects an item from the List
@Override
public void onListItemClick(ListView l, View v, int pos, long id) {
// Indicates the selected item has been checked
getListView().setItemChecked(pos, true);
// Inform the QuoteViewerActivity that the item in position pos has been selected
mListener.onListSelection(pos);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
// Set the ListSelectionListener for communicating with the QuoteViewerActivity
mListener = (ListSelectionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnArticleSelectedListener");
}
}
.........
}
So here in TitlesFragment we declare ListSelectionListener interface and checking in OnAttach() method if hosting Activity QuoteViewerActivity implements it.
Then our hosting activity implements this interface:
public class QuoteViewerActivity extends Activity implements ListSelectionListener {
public static String[] mTitleArray;
public static String[] mQuoteArray;
private QuotesFragment mDetailsFragment;
private static final String TAG = "QuoteViewerActivity";
....................................
// Called when the user selects an item in the TitlesFragment
@Override
public void onListSelection(int index) {
if (mDetailsFragment.getShownIndex() != index) {
// Tell the QuoteFragment to show the quote string at position index
mDetailsFragment.showQuoteAtIndex(index);
}
}
...................................
}
Sometimes it is needed to save fragment state. You might want to call addToBackStack(), in order to add the transaction to a back stack of fragment transactions. This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button. You can save the state during the fragment's onSaveInstanceState() callback and restore it during either onCreate(), onCreateView(), or onActivityCreated().
Also it would be great for you to read an article about possible problems with fragments. But every question raised in this article could be solved using standard methods.
P.S. To be simple fragments are mini-activities which allows more effectively use the space of large screens.