Learn how to use Android fragment and how to manage Fragment transaction with a step by step guide
This post describes how to use Android Fragment transaction to handle fragments. Android Fragment are useful when we want to support multiple screen size. To manage fragments we need a FragmentManager that help us to handle Android fragment trasaction between fragments. With Android fragment transaction we mean a sequence of steps to add, replace or remove fragments. In the last post we showed how to support multiple screen size and orientation using fragments. Now we want to go a bit further.
If you are not familiar with Fragment i suggest you to read before how to getting started with fragments. It can be useful, if you want to go a bit deeper, read also this tutorial about a real life example of using Fragment with Webview, before digging in the fragment transaction.
In Android there are two different methods to create fragment:
- static method
- dynamic method
Static method is when we “write” directly our fragment in XML file.
<fragment android:id=”@+id/listFragment” android:layout_width=”0dp” android:layout_height=”wrap_content” class=”com.survivingwithandroid.fragment.LinkListFragment” android:layout_weight=”2″/>
</LinearLayout>
[/xml]
In this case, our layout isn’t dynamic because we can’t manage the fragment at runtime. So if we want to make our layout dynamic (and this happen very often) we need to do it in another way. We have to use FrameLayout. With FrameLayout we can handle fragments as we need at runtime, but to do it we need a manager, in other words, a component that can handle fragments. This is FragmentManager. This component can add, replace and remove fragments at runtime. The layout above becomes:
[xml] <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” >
<FrameLayout android:id=”@+id/listFragment”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”/>
</RelativeLayout>[/xml]
So now we have all the freedom to “inject” in the FrameLayout our fragments.
Handle Android fragment transaction with FragmentManager
As we said before FragmentManager is the key component. Using FragmentManager we can discover (find) fragment inside our layout using findFragmentById or findFragmentByTag. While the first method is very simple and we use the common android id to discover the component, the second method (that uses tag) is unsual. A tag in a fragment is simply a “name” we give to the fragment so that we can find it later using that name. We are more interested in some other methods.
All the operation that are made by the FragmentManager happens inside a “transaction” (for that Android fragment transaction) like in a database operation. First, we can get the FragmentManger using the Activity method getFragmentManager(). Once we have the reference to this component we have to start the transaction in this way:
[java]FragmentManager fm = getFragmentManager();// Transaction start
FragmentTransaction ft = fm.beginTransaction();
…….
// Transaction commit
ft.commit();[/java]
At the end when we finished and we are ready to show our fragment we have to call the commit method that marks the end of the transaction. For example, if we remember the example we showed in the last post, and using a FrameLayout we can “insert” the link list in this way:
[java]public class MainActivity extends Activity implements ChangeLinkListener{@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
LinkListFragment llf = new LinkListFragment();
ft.replace(R.id.listFragment, llf);
ft.commit();
}
…
}[/java]
Suggested resources
Implement an Android location-aware app
How to use RecyclerView with CardView in Android
But…What kind of operation we can perform inside the Android fragment transaction?…Well we can:
- add a new fragment
- replace an existing fragment
- remove a fragment
If we remember the example we used last time every time a user clicks on a link the interface method onLinkChange is called. So in this method, we want to show how to perform the operation listed above.
[java]if (findViewById(R.id.fragPage) != null) {WebViewFragment wvf = (WebViewFragment) getFragmentManager()
.findFragmentById(R.id.fragPage);
// Part 1: Tablet and so on
if (wvf == null) {
System.out.println(“Dual fragment – 1”);
wvf = new WebViewFragment();
wvf.init(linkData.getLink());
// We are in dual fragment (Tablet and so on)
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//wvf.updateUrl(link);
ft.add(R.id.fragPage, wvf);
ft.commit();
}
else {
Log.d(“SwA”, “Dual Fragment update”);
wvf = new WebViewFragment();
wvf.init(linkData.getLink());
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragPage, wvf);
ft.commit();
//wvf.updateUrl(linkData.getLink());
}
}
else {
Log.d(“SwA”, “replace”);
FragmentTransaction ft = getFragmentManager().beginTransaction();
WebViewFragment wvf = new WebViewFragment();
wvf.init(linkData.getLink());
ft.replace(R.id.listFragment, wvf);
ft.commit();
}[/java]
If you compare this piece of code with the last example in the previous post you notice some differences. First, in the green part, we don’t start an activity anymore when the user clicks on a link but we simply replace the FrameLayout with a fragment showing the web page. Moreover, in the yellow part we don’t update the current fragment inside the FrameLayout but we create a new fragment and we replace the existing fragment with the one just created. The app behavior is always the same but we obtained this behavior in a different way, using dynamic fragments inside our layout.
If you run the app and start using it you can notice a “wired” behavior when you press the back button. We’d expect that the back button would bring us to the last web page visited but it isn’t like we supposed. When you press back button you come to the homepage.
Why?
Fragment Backstack
Well the behavior described above is normal because the back button acts at activity level, not at the fragment level. So our activity is the same while we replace fragments as the user interacts with the app. In this way when we tap on the back button we select the first activity on the activity stack, in our case the home. We don’t want this behavior but we want that when we click on the back button we go back in the fragments stack. We can achieve it adding the fragment to the backstack. We do it in this way:
[java]@Overridepublic void onLinkChange(LinkData linkData) {
System.out.println(“Listener”);
// Here we detect if there’s dual fragment
if (findViewById(R.id.fragPage) != null) {
WebViewFragment wvf = (WebViewFragment) getFragmentManager()
.findFragmentById(R.id.fragPage);
if(wvf == null) {
System.out.println(“Dual fragment – 1”);
wvf = new WebViewFragment();
wvf.init(linkData.getLink());
// We are in dual fragment (Tablet and so on)
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//wvf.updateUrl(link);
ft.add(R.id.fragPage, wvf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// Add to backstack
ft.addToBackStack(linkData.getName());
ft.commit();
}
else {
Log.d(“SwA”, “Dual Fragment update”);
wvf = new WebViewFragment();
wvf.init(linkData.getLink());
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.fragPage, wvf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
// Add to backstack
ft.addToBackStack(linkData.getName());
ft.commit();
//wvf.updateUrl(linkData.getLink());
}
}
else {
Log.d(“SwA”, “replace”);
FragmentTransaction ft = getFragmentManager().beginTransaction();
WebViewFragment wvf = new WebViewFragment();
wvf.init(linkData.getLink());
ft.addToBackStack(linkData.getName());
ft.replace(R.id.listFragment, wvf);
ft.commit();
}
}[/java]
We use the addToBackStack method of the FragmentTrasaction and we add every fragment to the backstack. In this way when we tap the back button we have the correct behavior. In this post, you learned how to handle Android fragment transaction and how to manage fragments.
This series about fragments is the best I found.
Thanks for sharing.
It not only shows knowledge about the matter but, what is more need in this job, expertise to explain it.
JJDS
Thx. I'm glad you liked my work
Good Example for fragment
What happens to the back stack on rotate? Is it set to null?
did you find out? I am currently struggling with this.
Can you share source code link ? Thanks
I am also having problem to understand this…Do u have any ideas?
I’m going to lool at this in depth soon. I will let you know what I find or come up with
I am also having problem to understand this…Do u have any ideas?
I'm going to lool at this in depth soon. I will let you know what I find or come up with
In json parsing, how authenticate digital signature in android, Please help me
In json parsing, how authenticate digital signature in android, Please help me
I want to make dicitonary app and ıt looks like word card so that I think I can use many fragment but I don’t know using many fragments.(2000 word card must be in project) can you help me?
I want to make dicitonary app and ıt looks like word card so that I think I can use many fragment but I don't know using many fragments.(2000 word card must be in project) can you help me?
Whose id is “R.id.fragPage” ? i didn’t found any variable or page of that id in your code.
ryt
can you explain why getBackStackEntryCount() method is return 0, please?
bcz you may not add them to backstack
ANDROID SUCKS
thank you my friend!
this has very helpfully for me!
Replace method replaces a fragment that already exists. Add method adds a new fragment.