Documente Academic
Documente Profesional
Documente Cultură
Index
o
o
o
o
o
o
o
o
o
Introduction
Background
Let's go
How to create the Fragment
Fragment Activity
Put the Fragment to an Activity
<fragment/>
Put the Fragment via Java code
Passing to data to a Fragment
Communicating between Fragment and Activity
Define the Interface
Attach the Listener
Call the Listener Methods
Implement the Listener
The Back Stack
Fragment Lifecycle
Warning
Futher Learning
Version History
Introduction
One Android application package can be run on a ton of devices including a phone,
tablet, PC, etc. In this article we will talk about how to make the app suitable for the big
screen, how can efficiently use the screen space, and tip and trick to make the app
better.
Background
When you run the app on the bigger screen, you may see that sometime there is a lot of
unused space because that app doesn't designed for the big screen. Android provides
the alternative resource mechanism to give the right resource to the right screen but it
not enough, Android also provide the Fragment that is used to create multi-pane
application with the Fragment you can put many screen of application into a one
screen.
Let's go
Android 3.0 introduce the Fragment, Fragment is a portable user interface that makes
possible in multi-pance UI creation, it is working like a View and you can add
many Fragments to a single Activity. Each Fragmenthas its own lifecycle that's
completely separates from the Activity lifecycle, because you can add or remove it at a
runtime.
For the compatibility with older versions of Android, Android also provides the support
library that allows you to use new features on the older Android version including
the Fragment. In this article we will focus on theFragment class from the support library
(don't worry both Fragment classes have a same behavior, only the package name that
different).
Master-Detail style application is the great example to learn about the Fragment. A lot of
application has a menu or link that when click it, the app will bring you to a new page or
show some data that relate to that menu/link, this is called Master-Detail user interface.
The picture above shows about Master-Detail user interface style on the phone. You will
see the master menu/link and the details are placed on the different pages.
The picture above shows about Master-Detail user interface style on the tablet. You will
see that on the tablet or a large screen device you can merge a master page and detail
page into a single page, yes it looks like a tab control (tab control is an implementation
of the master detail user interface).
Create 2 layouts for small and large screen as an alternative resource isnt a good idea
because you need to duplicate the logic part and when you want to add/remove
something to/from the page you need to do it on both layout files.
Think about creating a master page and a detail page separately with their own logic
part. Display only the master menu when the app run on the small screen but when the
app run on the large screen, attach the detail page to right of screen. This idea is called
multi-pane because you put many panes of UI on the single screen.
On the Android, showing many Activities on the screen is impossible, you have to
create Fragments for both pages and then attach it to the activity.
Task: Your task for this article is create the application to display the list of animals and
their details like pictures above. We will learn to create it together, Let' start
now!! Please create a new application without using any template (Add no
Activity) because we will create all parts of the app ourself.
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:20.0.0'
compile 'com.android.support:support-v4:20.0.0'
}
if you use the eclipse as IDE you need to copy the android-support-v4.jar file
fromSDK_LOCATION/extras/android/support/v4 to the libs directory of your project
(and make sure that the libs directory is exist in your project compile classpath).
Create the AnimalListFragment.
Collapse | Copy Code
import android.support.v4.app.Fragment;
public class AnimalListFragment extends Fragment{
}
I have created the layout that contains only LinearLayout and ListView because I want
to display the names of animals as a list.
When the Fragment was attached to the Activity, the Activity will call
the getView() method to get the Viewof Fragment. So, you need to create the View
before the getView() method call. Fragment provides the onCreateView() method for
using to construct its View. Now, implement the onCreateView() method by inflating
the layout and return it as a result of the method.
Collapse | Copy Code
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_animal_list,null);
return view;
}
Note: I have put the null value as the second parameter of inflate() method because I
don't want to attach the inflated View to any ViewGroup now. (host Activity of
this Fragment will attach the view automatically but if the View was attacthed, the
exception will thrown)
Next, create the Animal class to store the animal data.
Collapse | Copy Code
I have created the Animal class with only 3 attributes there are name, imageUrl, and
description. (but you can add more attributes if you want)
Then, back to the fragment, create mock Animal objects and store in the List. (In this
article I will use the mock data from the Wikipedia, but in the real application you should
avoid using static data)
Collapse | Copy Code
identified by their elbowed antennae and the distinctive node-like structure that forms their slender
waists.");
animals.add(ant);
Animal bird = new Animal();
bird.setName("Bird");
bird.setImageUrl("http://upload.wikimedia.org/wikipedia/commons/3/32/House_sparrow04.jpg");
bird.setDescription("Birds (class Aves or clade Avialae) are feathered, winged, two-legged, warmblooded, egg-laying vertebrates. Aves ranks as the tetrapod class with the most living species,
approximately ten thousand. Extant birds belong to the subclass Neornithes, living worldwide and
ranging in size from the 5 cm (2 in) bee hummingbird to the 2.75 m (9 ft) ostrich. The fossil record
indicates that birds emerged within the theropod dinosaurs during the Jurassic period, around 150
million years ago. On 31 July 2014, scientists reported details of the evolution of birds from theropod
dinosaurs. Most researchers agree that modern-day birds are the only living members of the
Dinosauria clade.");
animals.add(bird);
Animal cat = new Animal();
cat.setName("Cat");
cat.setImageUrl("http://upload.wikimedia.org/wikipedia/commons/thumb/b/bb/Kittyply_edit1.jpg/800pxKittyply_edit1.jpg");
cat.setDescription("The domestic cat is a small, usually furry, domesticated, and carnivorous
mammal. It is often called the housecat when kept as an indoor pet, or simply the cat when there is no
need to distinguish it from other felids and felines. Cats are often valued by humans for
companionship, and their ability to hunt vermin and household pests.");
animals.add(cat);
Animal dog = new Animal();
dog.setName("Dog");
dog.setImageUrl("http://upload.wikimedia.org/wikipedia/commons/1/13/Gaia_Basenji.jpg");
dog.setDescription("The domestic dog is a member of the Canidae family of the mammalian order
Carnivora. The term \"domestic dog\" is generally used for both domesticated and feral varieties. The
dog was the first domesticated animal and has been the most widely kept working, hunting, and pet
animal in human history. The word \"dog\" can also refer to the male of a canine species, as opposed to
the word \"bitch\" which refers to the female of the species.");
animals.add(dog);
return animals;
}
After that, override the onViewCreated() method. You should set data or Listeners
of Views in this method.
Collapse | Copy Code
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView animalListView = (ListView) view.findViewById(android.R.id.list);
}
Now, we need to create the Adapter for using to provide the data to ListView.
Collapse | Copy Code
List<Animal> animalList;
Context context;
public AnimalListAdapter(Context context,List<Animal> animalList){
this.context = context;
this.animalList = animalList;
}
@Override
public int getCount() {
return animalList==null?0:animalList.size();
}
@Override
public Animal getItem(int i) {
return animalList==null?null:animalList.get(i);
}
@Override
public long getItemId(int i) {
return 0;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
if(view==null)
view = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null);
Animal animal = getItem(i);
TextView nameTextView = (TextView)view.findViewById(android.R.id.text1);
nameTextView.setText(animal.getName());
return view;
}
}
I have created a simple adapter for list of Animal objects, this adapter will use
theandroid.R.layout.simple_list_item_1 layout to display the list item.
Next, back to the fragment create an instance of the adapter and set it to the ListView.
Note: To get the Context object from the Fragment, you can use
the getActivity() method to get the instance of Activity that this Fragment attached.
Collapse | Copy Code
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ListView animalListView = (ListView) view.findViewById(android.R.id.list);
AnimalListAdapter animalListAdapter = new AnimalListAdapter(getActivity(),generateAnimalList());
animalListView.setAdapter(animalListAdapter);
}
Next, we will create the activity for using to display our Fragment.
Fragment Activity
To put the Fragment to the Activity you need to have a FragmentManager object,
the FragmentManager allows you to manage Fragments on your Activity.
When there are two types of Fragment (native and support library), you need to get the
right FragmentManager object. If your Fragment derrived
from android.app.Fragment you need to use the FragmentManager
fromandroid.app.FragmentManager but if your Fragment derived
from android.support.v4.app.Fragmentyou need to use
the FragmentManager from android.support.v4.app.FragmentManager.
Android
3+'s android.app.Activity and android.support.v4.app.FragmentActivity provide
thegetFragmentManager() method that return the FragmentManager object
ofandroid.app.FragmentManager class but only
the android.support.v4.app.FragmentActivityprovides
the getSupportFragmentManager() method that return the FragmentManager object
from support library.
In this article, I will create the Activity that derived from the FragmentActivity class.
Note: ActionBarActivity is derived from FragmentActivity with an Action Bar
implementation.
Now, create the MainActivity that derived from FragmentActivity.
Collapse | Copy Code
Don't forget to add the MainActivity to the manifest (if you manually create
the Activity class file). And then set MainActivity as main and luancher.
AndroidManifest.xml
Collapse | Copy Code
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<fragment />
You can put the Fragment to the layout file directly by creating
the <fragment /> element, but by defining theFragment in the layout file you will can't
remove or replace it at runtime.
The most important attribute of fragment element is android:name attribute that is
refers to the full class path ofFragment that you want to display, other attributes are as
same as other view common attributes.
Create the layout file for MainActivity.
Collapse | Copy Code
setContentView(R.layout.activity_main);
Note: Don't forget please make sure that you have imported the
correct Fragment class, Android Studio will suggest to import
the android.app.Fragment but if you use the android.support.v4.app.Fragment you
need to import it manually.
Now you have a list of animals on the screen, next we will learn about adding
the Fragment dynamically in Java code.
If your app has some functions that need to change some Fragment at runtime for
example when user tap the settings menu at the right Fragment then the
left Fragment will replace with a settings Fragment, you need to do that via Java code.
To put the Fragment to an Activity via Java code you need to provide the container View
for a Fragment, I recommend you to use the FrameLayout becaase it's simple and
putting many Fragments in a single container isn't a good idea because it hard to
manage them.
Back to our app, when the user selects the animal the detail of that animal will show by
replace the animal list with animal detail on a phone but on a tablet we will show the
animal detail at the right of the screen. That meant our app will not use the
static Fragment(fragment element in layout).
In the MainActivity layout file replaces the fragment element with a FrameLayout.
Collapse | Copy Code
Then we need to create a one more layout file for the tablet.
Creating a layout directory for tablet
(you can simply create the layout-large directory in the res directory manually)
Right click on the res select New > Android resource directory
After that, select the Size in Available qualifiers and click >> button then change the
Screen size option to large.
Press OK button you will see the layout-large directory under the res directory.
Then create the Activity layout file in the layout-large directory with the same name
with layout for the phone.
Now, when run the application on tablet the layout file in the layout-large will inflate.
Next, add the view to the new layout. For the tablet, animal list and detail will show on
the same screen, you need to add 2 FrameLayouts on the Left and Right to display
the Fragments.
Note: You need to set the id of left FrameLayout as same as the FrameLayout in phone
layout and limit its width.
Collapse | Copy Code
</FrameLayout>
</LinearLayout>
Then get the FragmentManager instance for using to manage fragments. (I have used
the FragmentManagerform support library)
Collapse | Copy Code
fragmentTransaction.add(R.id.framelayout_left,animalListFragment);
fragmentTransaction.commit();
On the phone you will get the same result with the static Fragment.
When run the app on a tablet you will see that there is a blank area on the right
because we haven't created and add the detail Fragment.
Create a new Fragment called AnimalDetailFragment
Collapse | Copy Code
Then, create the view for this Fragment. Add an ImageView and a TextView.
Collapse | Copy Code
android:layout_margin="16dp"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:src="@drawable/android3"
/>
<TextView
android:id="@+id/textview_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_alignParentBottom="true"
android:text="@string/lorem_ipsum"
android:layout_below="@+id/imageview_animal"/>
</RelativeLayout>
</ScrollView>
I have used the ScrollView as root View because I don't know the content length if the
content is too long, the user can scroll down. And I have created the Lorem ipsum string
for using to mock the screen.
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_animal_detail,null);
return view;
}
After that, back to the MainActivity and put this Fragment to the right FrameLayout of
an Activitycontent view.
Note: You hate to check for the existence of the container View before putting
the Fragment because when you have many layout files for support many devices,
sometime some Views in some layout are not exist in another layout.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AnimalListFragment animalListFragment = new AnimalListFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.framelayout_left,animalListFragment);
if(findViewById(R.id.framelayout_right)!=null){
AnimalDetailFragment animalDetailFragment = new AnimalDetailFragment();
fragmentTransaction.add(R.id.framelayout_right,animalDetailFragment);
}
fragmentTransaction.commit();
}
On the tablet you will see the View of AnimalDetailFragment at the right of the screen.
Seem not beautiful? Try to change the background color of the animal list now!!
Copy the layout of AnimalListFragment to the layout-large and then edit the copy.
Collapse | Copy Code
Then when the user taps on the animal list item, it should to be highlighted to make
clear that this item was selected. Go to the animal list layout for tablet again and put
the listSelector attribute to the ListView.
Collapse | Copy Code
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle bundle = getArguments();
}
Make sure that the Animal class is derived from the Serializable marker interface
because to add the complex type to the bundle you need to make it serializable.
Then back to the AnimalDetailFragment and get the Animal object from the
arguments.
Note: To get something from the Bundle you need to know the key of them and should
to check the existence of that key before.
Collapse | Copy Code
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle bundle = getArguments();
if(bundle.containsKey("animal")) {
Animal animal = (Animal)bundle.getSerializable("animal");
}
}
After that, we will set the View data by using to Animal object if it exists.
I will use the Picasso library to load the animal image. On Android Studio you can add
this library by go to the build.gradle file in app directory and add the "compile
'com.squareup.picasso:picasso:2.3.4'" to a dependencies list. (If you use an eclipse as
IDE you need to download jar file and put it in a libs directory and make sure that the
libs directory is appear in the compile classpath)
Collapse | Copy Code
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:20.0.0'
compile 'com.android.support:support-v4:20.0.0'
compile 'com.squareup.picasso:picasso:2.3.4'
}
When the image is load from the internet, we need to add the internet access
permission to the manifest file.
Collapse | Copy Code
<uses-permission android:name="android.permission.INTERNET"/>
Back to the AnimalDetailFragment and try to set the image to ImageView and set the
description text.
Collapse | Copy Code
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Bundle bundle = getArguments();
if(bundle.containsKey("animal")) {
Animal animal = (Animal)bundle.getSerializable("animal");
ImageView animalImageView =
(ImageView)view.findViewById(R.id.imageview_animal);
TextView descriptionTextView = (TextView)
view.findViewById(R.id.textview_description);
Picasso.with(getActivity()).load(animal.getImageUrl()).placeholder(R.drawable.android3).i
nto(animalImageView);
descriptionTextView.setText(animal.getDescription());
}
}
Then go to the MainActivity and create the fake Animal object and pass it to
the AnimalDetailFragmentfragment.
Collapse | Copy Code
if(findViewById(R.id.framelayout_right)!=null){
Animal animal = new Animal();
animal.setName("Penguin");
animal.setImageUrl("http://www.emperor-penguin.com/penguin-chick.jpg");
animal.setDescription("Penguin Penguin Penguin Penguin Penguin Penguin Penguin
Penguin Penguin Penguin Penguin Penguin Penguin ");
Bundle bundle = new Bundle();
bundle.putSerializable("animal",animal);
AnimalDetailFragment animalDetailFragment = new AnimalDetailFragment();
animalDetailFragment.setArguments(bundle);
fragmentTransaction.add(R.id.framelayout_right,animalDetailFragment);
}
OnFragmentInteractionListener listener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
if(activity instanceof OnFragmentInteractionListener){
listener = (OnFragmentInteractionListener) activity;
}
}
Now, when you put the Fragment on the Activity you will get
the instance ofOnFragmentInteractionListener if that Activity derived from this
interface.
When the Fragment was detached from the Activity you should to remove the listener
too to prevent accidently call the listener method. Override the onDetach() method and
set the listener to null.
Note: onDetach() method will call when the Fragment was detached from the Activity.
Collapse | Copy Code
@Override
public void onDetach() {
super.onDetach();
listener = null;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Note: Please make sure the listener is not null then call
the onAnimalSelected() method;
The IDE will ask you to implements the method(s) of that interface, confirm it and you
will get theonAnimalSelected() method. (if an IDE doesn't ask to implement, please do
it yourself)
Collapse | Copy Code
@Override
public void onAnimalSelected(Animal animal) {
@Override
public void onAnimalSelected(Animal animal) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
int containerViewId = R.id.framelayout_left;
if(findViewById(R.id.framelayout_right)!=null)
containerViewId = R.id.framelayout_right;
Bundle bundle = new Bundle();
bundle.putSerializable("animal",animal);
AnimalDetailFragment animalDetailFragment = new AnimalDetailFragment();
animalDetailFragment.setArguments(bundle);
fragmentTransaction.replace(containerViewId,animalDetailFragment);
fragmentTransaction.commit();
}
You will see that when you select the animal that detail will change to math that
animal!!
On the phone when you tab on the animal name the detail of the animal will show but
can't go back to the animal list T^T
@Override
public void onAnimalSelected(Animal animal) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
int containerViewId = R.id.framelayout_left;
if(findViewById(R.id.framelayout_right)!=null)
containerViewId = R.id.framelayout_right;
Now, you can press the back button on the detail page to back to the animal list.
Our app is almost complete but there is something that you should know.
Fragment Lifecycle
onPause() - Called when the Fragment is no longer interacting with the user.
Should save data at this state.
onStop() - Called when the Fragment is no longer visible.
onDestroyView() - Called when the View was destroyed.
onDestroy() - Called to do final cleanup.
onDetach() - Called when the Fragment is no longer attached to the Activity.
Warning
When you run our app on the phone, you will found that if you select an animal then
rotate the device, the animal list will place over the detail page.
Every FragmentTransaction will store for re-execute every time when the Activity recreate for example when the device orientation change the Activity will recreate then
every FragmentTransaction that do before screen rotate will re-execute automatically.
Sound good but the problem is if you add the Fragment inonCreate() method of
an Activity, your Fragment will and place over the Fragment from the re-execute
ofFragmentTransaction.
You can solve this problem by check the Bundle parameter of Activity.onCreate(), if
the Bundle is null that meant this is first time that onCreate() called, you can do
a FragmentTransaction. But If the Bundle is not null, don't do
any FragmentTransaction to avoid the problem
Collapse | Copy Code
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* Inflate View and restore saved state here*/
if(savedInstanceState!=null)
return;
/* FragmentTransaction here*/
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState!=null)
return;
AnimalListFragment animalListFragment = new AnimalListFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.framelayout_left,animalListFragment);
if(findViewById(R.id.framelayout_right)!=null){
Animal animal = new Animal();
animal.setName("Penguin");
animal.setImageUrl("http://www.emperor-penguin.com/penguin-chick.jpg");
animal.setDescription("Penguin Penguin Penguin Penguin Penguin Penguin Penguin Penguin
Penguin Penguin Penguin Penguin Penguin ");
Bundle bundle = new Bundle();
bundle.putSerializable("animal",animal);
AnimalDetailFragment animalDetailFragment = new AnimalDetailFragment();
animalDetailFragment.setArguments(bundle);
fragmentTransaction.add(R.id.framelayout_right,animalDetailFragment);
}
fragmentTransaction.commit();
}
Further Learning
The thing that is more important than what you learn today is what you will learn
tomorrow. In this article I talk about only a basic of Fragment usage but there are
many implementations of Fragment that interesting and you should learn it.
Tabs - Most of tabs implementation use the Fragment as content.
ViewPager - Want to make a horizontal page scroll? ViewPager is the answer, it work like
a ListView but the items in ViewPager are Fragments.
Navigation Drawer - There are many apps that have a navigation drawer such as
Facebook. Move the side menu to the navigation drawaer is a good idea.
Hope you enjoy your Android Application Developer life.
History
License
This article, along with any associated source code and files, is licensed under The Code
Project Open License (CPOL)