I finished the Coursera course on Programming Mobile Applications for Android Handheld Systems. One of the example code from the course teaches us how to us fragments. Basically what it does is it splits the screen into two fragments, one for book titles and one for quotes from the book. If a user clicks on a title in a fragment on the left, the quote associated from the book is displayed in fragment on the right.
This is the code for the mainActivity:
package course.examples.Fragments.StaticLayout;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import course.examples.Fragments.StaticLayout.TitlesFragment.ListSelectionListener;
public class QuoteViewerActivity extends Activity implements
ListSelectionListener {
public static String[] mTitleArray;
public static String[] mQuoteArray;
private QuotesFragment mDetailsFragment;
private static final String TAG = "QuoteViewerActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTitleArray = getResources().getStringArray(R.array.Titles);
mQuoteArray = getResources().getStringArray(R.array.Quotes);
setContentView(R.layout.main);
mDetailsFragment = (QuotesFragment) getFragmentManager()
.findFragmentById(R.id.details);
}
@Override
public void onListSelection(int index) {
if (mDetailsFragment.getShownIndex() != index) {
mDetailsFragment.showQuoteAtIndex(index);
}
}
This is the code for the Quote fragment:
package course.examples.Fragments.StaticLayout;
import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class QuotesFragment extends Fragment {
private TextView mQuoteView = null;
private int mCurrIdx = -1;
private int mQuoteArrayLen;
private static final String TAG = "QuotesFragment";
public int getShownIndex() {
return mCurrIdx;
}
public void showQuoteAtIndex(int newIndex) {
if (newIndex < 0 || newIndex >= mQuoteArrayLen)
return;
mCurrIdx = newIndex;
mQuoteView.setText(QuoteViewerActivity.mQuoteArray[mCurrIdx]);
}
@Override
public void onAttach(Activity activity) {
Log.i(TAG, getClass().getSimpleName() + ":entered onAttach()");
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, getClass().getSimpleName() + ":entered onCreate()");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
**return inflater.inflate(R.layout.quote_fragment, container, false);**
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mQuoteView = (TextView) getActivity().findViewById(R.id.quoteView);
mQuoteArrayLen = QuoteViewerActivity.mQuoteArray.length;
}
This is the code for the title fragment:
package course.examples.Fragments.StaticLayout;
import android.app.Activity;
import android.app.ListFragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class TitlesFragment extends ListFragment {
private ListSelectionListener mListener = null;
private static final String TAG = "TitlesFragment";
public interface ListSelectionListener {
public void onListSelection(int index);
}
@Override
public void onListItemClick(ListView l, View v, int pos, long id) {
getListView().setItemChecked(pos, true);
mListener.onListSelection(pos);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (ListSelectionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnArticleSelectedListener");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, getClass().getSimpleName() + ":entered onCreate()");
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i(TAG, getClass().getSimpleName() + ":entered onCreate()");
**return super.onCreateView(inflater, container, savedInstanceState);**
}
@Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
setListAdapter(new ArrayAdapter<String>(getActivity(),
R.layout.title_item, QuoteViewerActivity.mTitleArray));
}
MainActivity xml
<?xml version="1.0" encoding="utf-8"?>
<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.TitlesFragment" />
<fragment
android:id="@+id/details"
android:layout_width="0px"
android:layout_height="match_parent"
android:layout_weight="2"
class="course.examples.Fragments.StaticLayout.QuotesFragment" />
</LinearLayout>
Quote Fragments xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/quoteView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dip"
android:textSize="32sp" >
</TextView>
</LinearLayout>
Title fragment xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?android:attr/activatedBackgroundIndicator"
android:orientation="vertical"
android:padding="5dip"
android:textSize="32sp" >
</TextView>
My question is why does the method under onCreateView differ under Quote fragment and Title fragment? QuoteFragment is return inflater.inflate(R.layout.quote_fragment, container, false); TitleFragment is return super.onCreateView(inflater, container, savedInstanceState);
onCreateView() is called by Android once the Fragment should inflate a view. onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null .
These files contain only the onCreateView() method to inflate the UI of the fragment and returns the root of the fragment layout. If the fragment does not have any UI, it will return null.
onCreateView(LayoutInflater, ViewGroup, Bundle) creates and returns the view hierarchy associated with the fragment. onActivityCreated(Bundle) tells the fragment that its activity has completed its own Activity.
Retained fragments take advantage of the fact that a fragment's view can be destroyed and recreated without having to destroy the fragment itself. During a configuration change, the FragmentManager first destroys the views of the fragments in its list.
Because QuotesFragment
extends Fragment
that doesn't have layout by default and a user has to inflate and return his own layout. This is how onCreateView
method of a Fragment
looks like:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return null;
}
TitlesFragment
extends ListFragment
that has a layout by default that contains ListView
for items, TextView
for a label when a list is empty and ProgressBar
. In this case the user doesn't have to return his own view and can return an object obtained by the super call. onCreateView
of the ListFragment
:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(com.android.internal.R.layout.list_content,
container, false);
}
Because a ListFragment
already has a default layout containing just the ListView
used by it. You can inflate a custom layout like with the other Fragment
, but it is not necessary. If you just want to use a ListView than you can return super.onCreateView(...)
.
If you would want to use a custom layout you have to remember to use this id for your ListView
:
android:id="@android:id/list"
You have
return super.onCreateView(inflater, container, savedInstanceState);
ListFragment
has a default layout that consists of a single list view. So there is no need to inflate a custom layout if you don't want any other views displayed on the screen.
Look at the source of ListFragment
. Your Fragment
class extends ListFragment
.
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4.2_r1/android/app/ListFragment.java/
189
190 @Override
191 public View onCreateView(LayoutInflater inflater, ViewGroup container,
192 Bundle savedInstanceState) {
193 return inflater.inflate(com.android.internal.R.layout.list_content,
194 container, false);
195 }
Line 193 indicates the default layout inflated
Further you have
public class QuotesFragment extends Fragment {
and this
return inflater.inflate(R.layout.quote_fragment, container, false);
What you are doing is inflating a custom layout called quote_fragment.xml
Edit:
Suppose you want to display other views in ListFragment
you need to inflate a custom layout. And that layout must have ListFragment
with id @android:id/list
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With