Fragments
Mobile Computing - Android
Introduction
There are many, many different sized
Android devices, making UI design a
challenge.
Fragments are an attempt to address this
problem. Instead of just a single layout, the
user interface can be divided into parts,
each represented by a Fragment.
Fragments combine to form the entire user
interface, and they are modular. They can
be reused in different Activities, and
swapped in and out of a single Activity
while it is running.
Note that Fragments are always part of an
Activity, and the Activity is still in charge:
while it is running it can add or remove
Fragments as needed.
1 Activity with multiple Fragments
The Fragment Lifecycle
In addition to its own UI, a Fragment have its own
lifecycle, intertwined with the Activity with which it is
associated.
Lifecycle methods include those found in the
Activity:
onCreate(), onStart(), onResume(), onPause(),
onSaveInstanceState(), on Stop() and onDestroy()
In addition, a Fragment has its own unique methods
Fragment Lifecycle
Methods
onAttach(Activity acty) -- called when a Fragment
is attached to an Activity (before onCreate())
onCreateView() - called before the Fragment
enters the resumed state
onViewCreated()
onActivityCreated() -- called when the associated
activity has been created and this Fragment's
view has been instantiated
onDestroyView() -- the counterpart to
onCreateView()
onDetach() -- called after onDestroy(), just before
the Fragment becomes eligible for garbage
collection
Three Famous Fragments
DialogFragment
ListFragment
Displays a dialog box
We will look at some of
these in detail later on
displays a ListView inside a Fragment (like the
ListActivity -- also includes OnListItemClick())
PreferenceFragment
Useful for creating settings for your app
Responsive Design
Often seen with a master/detail interface
Tap on a row and see detailed information about that item. The detailed fragment can
be either in the same activity (on a sufficiently wide screen), or in a different activity.
activity_item_twopane.xml
The ItemListActivity has 2
layouts, depending on the
size of the screen. If width
permits, it uses
activity_item_two_pane.xml,
with a fragment for the list
view and for the detailed view.
Otherwise, it uses
activity_item_list.xml, and a
second activity,
ItemDetailActivity, presents
the same detail Fragment as
above.
Creating A Fragment
1.Make a new Fragment (File >> New File >> Fragment >> Blank)
2.Override onCreateView() to create the Fragment's View
3.Edit the layout xml as needed (we'll just add a Button)
Aside: Android provides 5 Fragment subclasses which we will explore
later. Here the goal is to make our own subclass.
Overriding onCreateView()
public class BlankFragment extends Fragment {
public BlankFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_blank, container, false);
Button btn = (Button) view.findViewById(R.id.clickMeBTN);
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v){ /* .... */ }
});
return view;
}
}
A Fragment's View must be returned by its onCreateView() method
LayoutInflater -- an object capable of taking an xml file, and making a View out of it
ViewGroup -- the ViewGroup (i.e., the activity's view) in which the fragment will be
embedded
Bundle -- data (if the fragment had been previously paused and is now resuming)
The Official Word on the
Subject (aka the API)
Your Fragment must override onCreateView()
Optional? If your Fragment extends ListFragment (discussed later) you
need not implement onCreateView() it will give you a ListView for free :-)
Adding a Fragment to an
Activity Using XML
Each fragment must appear in an activity's layout.
One way to do this is by specifying a fragment in an
activity's layout file with the <fragment> tag,
specifying the Fragments fully qualified name in the
android:name attribute (e.g.,
org.mprogers.SuperduperFragment).
The Fragment's layout will appear in the location
specified by the fragment tag, with the appropriate
size.
An Example
res/layout-large/news_articles.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
Specify fragments
in the activity's
layout file
<fragment
android:name="com.example.android.fragments.HeadlinesFragment"
android:id="@+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment
android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
Load the layout in
the usual way ...
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
}
}
An Example, Enlarged
HeadlinesFragment
ArticleFragment
Placing Fragments
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<fragment
android:id="@+id/topFRG"
android:name="org.mprogers.fragmento.TopFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="26dp"
android:layout_marginTop="24dp"/>
<fragment
android:id="@+id/bottomFRG"
android:name="org.mprogers.fragmento.BottomFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/fragment1"
android:layout_below="@+id/fragment1"
android:layout_marginTop="31dp"
tools:layout="@layout/bottom_fragment" />
</RelativeLayout>
1. Drag <fragment> in xml
editor (under Custom)
2. When prompted choose
an existing Fragment
subclass.
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Part II: Dynamic Fragments
The fragments that we have placed thus far are
known as static -- they appear where the
<fragment> tag appears in the Activity's layout,
and cannot be altered at runtime.
Building a More Flexible UI
If you place a fragment with the <fragment> tag in
the layout file, it is stuck there: that fragment cannot
be swapped out.
Q: Why would you want to swap out fragments?
A: 1. It is one solution to the problem of designing
multi-pane applications for tablets and single-pane
applications for smaller screens.
2. Swapping out fragments makes it possible to
add customized animations.
When There's Room
On a tablet, fragment A and B can
appear simultaneously
On a smaller device, tapping on
fragment A should lead to fragment B
It could be done with 2 activities, each
one hosting one fragment: tapping on
Fragment A would require an intent to
startActivity() for Fragment B.
This could be done with 1 Activity,
swapping out fragment A for B when
necessary: a concrete example of
Answer 1 on the previous slide.
Adding a Fragment at
Runtime
Ask a FragmentManager to make a FragmentTransaction
Can add(), remove(), replace() an existing fragment
The fragment must have a designated location, a container
view in the activity's layout.
A FrameLayout is a good choice if only 1 fragment will be
visible at a time
Add the initial fragment in the activity's onCreate(),
otherwise the user won't see anything :-0
An Example
Instead of 2 static fragments, just add 1, programmatically, in a FrameLayout, and
replace it when tapping on the article headline
The activity's layout will be considerably simpler
res/layout-large/news_articles.xml (when the layout is large)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.android.fragments.HeadlinesFragment"
android:id="@+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.android.fragments.ArticleFragment"
android:id="@+id/article_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
/res/layout/news_articles.xml
When the layout isn't so large ...
<FrameLayout xmlns:android="http://.../res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles); // something slick is happening here ...
An Example, Continued
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null)
return;
// Create a new Fragment to be placed in the activity layout
HeadlinesFragment firstFragment = new HeadlinesFragment();
The app contains 2
layouts, the one used
depends on the device
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.fragment_container, firstFragment);
transaction.commit();
}
}
where to add
what to add
Replacing Fragments
public void onArticleSelected(int position) {
// Create fragment and give it an argument specifying the article it should show
ArticleFragment articleFragment = new ArticleFragment();
Bundle args = new Bundle();
args.putInt(ArticleFragment.ARG_POSITION, position);
articleFragment.setArguments(args);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, articleFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
In Activity - triggered when
user clicks on a ListItem
Fragment => Activity
Communication
Fragments don't communicate directly. Instead, they talk
to the Activity in which they are embedded.
Preferred technique:
interface1
interface2
interface1
interface2
1. Define an interface, in the fragment, that the activity
implements
2. Capture the activity in the fragment's
onAttach(Activity activity) method
3. Invoke the interface method in the activity when
necessary.
Alternatively, to call an activity's public methods directly
(or don't mind continually getting the activity), the
Fragment defines this helpful method:
public Activity getActivity() // a fragment always knows
which activity it belongs to
The Essence of the
Communication
class MyFragment extends Fragment {
interface MyInterface {
void sayHi();}
}
MyInterface myInterface;
public void onAttach(Activity activity){
myInterface = (MyInterface)activity;
}
public void buttonHandler(View v){
myInterface.sayHi();
}
}
class MyActivity extends Activity
implements MyFragment.MyInterface{
void sayHi(){
Debug.Log(Hello);
}
}
Life Without Interfaces
class MyFragment extends Fragment {
//interface MyInterface {
//void sayHi();}
//}
// MyInterface myInterface;
MyActivity myActivity;
public void onAttach(Activity activity){
//myInterface = (MyInterface)activity;
myActivity = activity;
}
public void buttonHandler(View v){
//myInterface.sayHi();
myActivity.sayHi();
}
class MyActivity extends Activity
implements MyInterface{
void sayHi(){
Debug.Log(Hello);
}
}
Activity => Fragment
Communication
If a fragment has been defined with a <fragment>
tag, i.e., it is static, then
1. Access it in the Activity with the
FragmentManager method:
public Fragment findFragmentById(int id)
2. Call its public methods directly
An Example
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;// this will be the Activity
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity; // gotcha!
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
Fragment Constructors
This sounds vaguely medical ...
Google recommends that a Fragment only use a default
constructor
So how do you initialize a Fragment to get into a particular state?
Embed whatever initial information you need in a Bundle, and
supply that to the Fragment via setArguments()
Retrieve them via getArguments()
In the Android Studio template, this happens in a factory method
called newInstance()
Arguments @ Work
public static BlankFragment newInstance(String param1, String param2) {
BlankFragment fragment = new BlankFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
Log.d("TAG", "Greetings from newInstance()");
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
Log.d("TAG", String.format("Greetings from onCreate() where
params are %s & %s", mParam1, mParam2));
}
An ICE: The Fragments
Template
Create an Android Studio project with a single
Fragment
Add a ListFragment and a BlankFragment
Examine the code ...
ListFragment
ListFragment extends Fragment
Recall how ListActivities worked:
a ListActivity came with a ListView "for free"
used setListAdapter() to establish the adapter (source of the ListView's
child views)
overrode onListItemClick() method to handle clicks
if all we wanted was a ListView, we didn't need to create a layout -- but if we
wanted a custom layout, we had to make it, and
A ListFragment works the same way
We've already used the ListFragment in the FragmentBasics-Gradle project
Exercises
What are Fragments?Why are they necessary? What is the relationship
between a Fragment and an Activity?
Whatis the Fragment hierarchy?
Which comes first, an Activity or a Fragment?
Write a Fragment's onCreateView() method. Assume that its layout is
defined in R.layout.my_fragment.
Describe the Fragment lifecycle.
How is the Fragment lifecycle affected by the Activity lifecycle? What
happens to a Fragment when an Activity is paused? destroyed?
What are the twoways of adding a Fragment to an Activity?
Exercises
Write the code to add a Fragment called SkinnyFragment -- with a layout in
R.layout.skinny_fragment -- to an Activity that uses a FrameLayout with an id of
android:id="@+id/fragment_container". Do so using a <fragment> tag, and
by using a FragmentManager().
What transactions can be performed with a FragmentManager?
Write the code necessary to instantiate a Fragment called FrangoMints and display
it in an activity's FrameLayout (assume the same is as in question 7)
Write the code necessary to instantiate a second Fragment called AndesMintes
and replace the current Fragment with it
Can you use a <fragment> tag with a FragmentManager?
What is the difference between /res/layout and /res/layout-large? Between /res/
layout-port and/res/layout-land?
Exercises
Can you have 2 files, both named my_layout.xml, defined in both /res/layout-port
and /res/layout-land? If so, when would each file be utilized?
What is the purpose of getArguments() and setArguments()?
Describe how ListFragments and WebViewFragments work.
Name the key methods of the WebViewFragment class What does loadUrl() do?
What does setWebClient() do?
What is the purpose of android.permission.INTERNET? Where, exactly is that
placed in an AndroidManifest.xml file?
Describe how Fragments communicate with Activities using interfaces.
What is the advantage of this communication?
Describe how Activities communicate with Fragment using findFragmentById()
Resources
http://developer.android.com/training/basics/fragments/index.html
http://developer.android.com/guide/components/fragments.html
http://developer.android.com/guide/practices/screens_support.html
http://stackoverflow.com/questions/6677136/member-variables-vssetarguments-in-fragments
http://stackoverflow.com/questions/6091194/how-to-handle-button-clicksusing-the-xml-onclick-within-fragments
http://stackoverflow.com/questions/19844244/fragmentbasics-gives-nullpointer-exception
http://stackoverflow.com/questions/26805863/android-studio-directory-creation