Learn how to use Fragment in Android. Explore how to handle Fragment lifecycle using a real example
In this post we want to explain how to use fragment in Android with a real example. In this post we want to go deeper and create an example that helps us to understand better how we can use fragments. The post will describe how to use Framelayout and how to exchange data between fragments.
The last post introduced the main concepts about Fragment lifecycle in Android and how we can use it to support multiple screen. We described their lifecycle and how we can use it.
As example how to use fragment in Android, we want to create a simple application that is built by two fragments:
- one that shows a list of links and
- another one that shows the web page in a WebView.
We can suppose we have two different layouts one for portrait mode and one for landscape. In the landscape mode we want something like the image below:
while, in the portrait mode we want something like:
CREATE Android UI LAYOUT
The first step we have to do is creating our layout. As we said we need two different layout one for portrait and one for landscape. So we have to create two xml file under res/layout (for portrait mode) and one under res/layout-land (for landscape mode). Of course we could customize more our layout including other specs but it is enough for now. These two files are called activity_layout.xml.
[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” >
<fragment android:id=”@+id/listFragment”
android:layout_width=”wrap_content”
android:layout_height=”wrap_content”
class=”com.survivingwithandroid.fragment.LinkListFragment”/>
</RelativeLayout>[/xml]
This one is for the portrait mode and as we notice we have only one fragment containing just the link list. We will see later how to create this fragment. We need, moreover, another layout as it is clear from the pic above, the one that contains the WebView.
[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”>
<WebView android:id=”@+id/webPage”
android:layout_height=”wrap_content”
android:layout_width=”wrap_content”/>
</LinearLayout>
[/xml]
For the landscape mode, we have something very similar plus the FrameLayout component in the same layout.
[xml]<LinearLayout 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”
android:orientation=”horizontal” >
<fragment android:id=”@+id/listFragment”
android:layout_width=”0dp”
android:layout_height=”wrap_content”
class=”com.survivingwithandroid.fragment.LinkListFragment”
android:layout_weight=”2″/>
<FrameLayout android:id=”@+id/fragPage”
android:layout_width=”0dp”
android:layout_height=”match_parent”
android:layout_weight=”4″
/>
</LinearLayout>[/xml]
Create link list fragment
As you have already noticed both layout have a common fragment that is called LinkListFragment. We have to create it. If you didn’t read already the post explaining the lifecycle it is time you give a look. In this case we don’t have to override all the methods in the fragment lifecycle, but those important to control its behaviour. In our case we need to override:
- onCreateView
- onAttach
In this fragment we use a simple ListView to show the links and a simple adapter to customize the way how the items are shown. We don’t want to spend much time on how to create a custom adapter because it is out of this topic you can refer here to have more information. Just to remember in the onCreateView fragment method we simply inflate the layout and initialize the custom adapter. As XML layout to inflate in the fragment we have:
[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” >
<ListView android:id=”@+id/urls”
android:layout_height=”match_parent”
android:layout_width=”wrap_content”/>
</LinearLayout>[/xml]
while in the method looks like:
[java]@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(“SwA”, “LV onCreateView”);
View v = inflater.inflate(R.layout.linklist_layout, container, false);
ListView lv = (ListView) v.findViewById(R.id.urls);
la = new LinkAdapter(linkDataList, getActivity());
lv.setAdapter(la);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parentAdapter, View view, int position,
long id) {
LinkData data = ( (LinkAdapter) la ).getItem(position);
( (ChangeLinkListener) getActivity()).onLinkChange(data.getLink());
}
});
return v;
}[/java]
In this method we simply set our custom adapter and the set the listener when the user clicks on an item. We will cover this later (if you are curious see Inter fragment communication).
In the onAttach method, we verify that the activity that holds the fragment implements a specific interface.
[java]@Overridepublic void onAttach(Activity activity) {
// We verify that our activity implements the listener
if (! (activity instanceof ChangeLinkListener) )
throw new ClassCastException();
super.onAttach(activity);
}[/java]
We will clarify why we need this control later.
Fragment in Android: COMMUNICATION
Basically in our example we have two fragments and they need to exchange information so that when user select an item in the fragment 1 (LinkListFragment) the other one (WebViewFragment) shows the web page corresponding to the link. So we need to find a way to let these fragments to exchange data.
On the other way we know that a fragment in Android is a piece of code that can be re-used inside other activity so we don’t want to bind our fragment to a specific activity to not invalidate our work. In Java if we want to decouple two classes we can use an interface. So this interface solution fits perfectly. On the other hand we don’t want that our fragment exchange information directly because each fragment can rely only on the activity that holds it. So the simplest solution is that the activity implements an interface.
So in our case, we define an interface called ChangeLinkListener that has only one method:
[java]public interface ChangeLinkListener {public void onLinkChange(String link);
}[/java]
We have, moreover, to verify that our activity implements this interface to be sure we can call it. The best place to verify it is in the onAttach method (see above) and at the end we need to call this method when the user selects an item in the ListView:
[java]lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Override
public void onItemClick(AdapterView&lt;?&gt; parentAdapter, View view, int position,
long id) {
LinkData data = ( (LinkAdapter) la ).getItem(position);
(ChangeLinkListener) getActivity()).onLinkChange(data.getLink());
}
});[/java]
Programming main activity: Find fragment
By now we talked about fragment in Android only, but we know that fragments exists inside a “father” activity that control them. So we have to create this activity but we have to do much more.
As we said before this activity has to implements a custom interface so that it can receive data from the LinkListFragment. In this method (onLinkChange) we have somehow to control if we are in landscape mode or in portrait mode, because in the first case we need to update the WebViewFragment while in the second case we have to start another activity. How can we do it? The difference in the layout is the presence of the FrameLayout. If it is present it means we are in landscape mode otherwise in portrait mode. So the code in the onLinkChange method is:
public void onLinkChange(String link) {
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(link);
// We are in dual fragment (Tablet and so on)
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
//wvf.updateUrl(link);
ft.replace(R.id.fragPage, wvf);
ft.commit();
}
else {
Log.d(“SwA”, “Dual Fragment update”);
wvf.updateUrl(link);
}
}
else {
System.out.println(“Start Activity”);
Intent i = new Intent(this, WebViewActivity.class);
i.putExtra(“link”, link);
startActivity(i);
}
}[/java]
Let’s analyse this method. The first part (line 5) verify that exists the FrameLayout. If it exists we use the FragmentManager to find the fragment relative to the WebViewFragment. If this fragment is null (so it is the first time we use it) we simply create it and put this fragment “inside” the FrameLayout (line 7-20). If this fragment already exists we simply update the url (line 23). If we aren’t in landscape mode, we can start a new activity passing data as an Intent (line 28-30).
WEBVIEW FRAGMENT AND WEBVIEWACTIVITY: The web page
Finally, we analyze the WebViewFragment. It is really simple, it just overrides some Fragment method to customize its behavior:
[java]public class WebViewFragment extends Fragment {private String currentURL;
public void init(String url) {
currentURL = url;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(“SwA”, “WVF onCreateView”);
View v = inflater.inflate(R.layout.web_layout, container, false);
if (currentURL != null) {
Log.d(“SwA”, “Current URL 1[“+currentURL+”]”);
WebView wv = (WebView) v.findViewById(R.id.webPage);
wv.getSettings().setJavaScriptEnabled(true);
wv.setWebViewClient(new SwAWebClient());
wv.loadUrl(currentURL);
}
return v;
}
public void updateUrl(String url) {
Log.d(“SwA”, “Update URL [“+url+”] – View [“+getView()+”]”);
currentURL = url;
WebView wv = (WebView) getView().findViewById(R.id.webPage);
wv.getSettings().setJavaScriptEnabled(true);
wv.loadUrl(url);
}
private class SwAWebClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
}
}[/java]
In the onCreateView method we simply inflate our layout inside the fragment and verify that the url to show is not null. If so we simply show the page (line 15-30). In the updateUrl we simply find the WebView component and update its url.
In the portrait mode we said we need to start another activity to show the webpage, so we need an activity (WebViewActivity). It is really simple and i just show the code without any other comment on it:
[java]public class WebViewActivity extends FragmentActivity {@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebViewFragment wvf = new WebViewFragment();
Intent i = this.getIntent();
String link = i.getExtras().getString(“link”);
Log.d(“SwA”, “URL [“+link+”]”);
wvf.init(link);
getFragmentManager().beginTransaction()
.add(android.R.id.content, wvf).commit();
}
}[/java]
Source code @ github.
Nice post.Give it up. Thanks for share this article. For more visit:Web App Development
expandable listview layout how to set in tablet screen using fragment?
pls help me ASAP. thank u in advance.
If i get your question correctly, you want to replace ListView with ExpandableListView. In this case you have to chage in the layout the widget. If you want to know how to use ExpandableListView you can give a look here https://www.survivingwithandroid.com/2013/01/android-expandablelistview-baseexpandablelistadapter.html
Sources don't work, missing class name in fragments (in activity_main.xml).
You have to specify which class the deflater has to instantiate.
I will check the source code to know if i missed some classes. Thx for your support
I may be missing something, but where are LinkData, LinkAdapter, etc found? Because I can't find what class to import here
There are in the source code. https://github.com/survivingwithandroid/Surviving-with-android/tree/master/FragmentTutorialWeb/src/com/survivingwithandroid/fragment/model for example. What r u missing?
Anyone having trouble getting the source to run- You need to add this line-
class="com.survivingwithandroid.fragment.LinkListFragment"
to the fragment element in each of the activity_main.xml files under reslayout and reslayout-land
It is shown in the code listing above but not included in the source code download on github.
Hope this helps.
(Portrait mode works as expected but I am still not seeing two fragments in the landscape view)
Ok, landscape mode is not working in the github source because layout-landactivity_main.xml file contains an incorrect android:layout_width for fragment element.
It should be- android:layout_width="0dp"
Again the code block in the article shows the correct version but the github source is incorrect.
I tried to run this on Eclipse Juno with Android 4.4.2 SDK and got this error
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.survivingwithandroid.fragment/com.survivingwithandroid.fragment.MainActivity}: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Can the author please help?
I managed to resolve the problem by editing AndroidManifest.xml with this line
Hope that author will update example for Fragment V4.
Sorry.. I was running another example. The problem is not fixed.. Please help!
I tried to run this on Eclipse Juno with Android 4.2 SDK and got this error
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.survivingwithandroid.fragment/com.survivingwithandroid.fragment.MainActivity}: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Can the author please help?
i tried but iam getting an error at init() and update() which are accessing with wvf WebViewFragment
Sorry i am getting Exception as
ava.lang.RuntimeException: Unable to start activity ComponentInfo{com.survivingwithandroid.fragment/com.survivingwithandroid.fragment.MainActivity}: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Sorry i am getting Exception as
ava.lang.RuntimeException: Unable to start activity ComponentInfo{com.survivingwithandroid.fragment/com.survivingwithandroid.fragment.MainActivity}: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #12: Error inflating class fragment
Hi people !! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi !! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi people !! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi !! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi ! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi ! Code is working !! just one change in your Main Activity: MainActivity extends FragmentActivity
Hi!
I just replace extends class but LogCat still give me the same error and additional – Caused by: java.lang.NullPointerException: name == null
Help, please
Hi!
I just replace extends class but LogCat still give me the same error and additional – Caused by: java.lang.NullPointerException: name == null
Help, please
I am tried to load url in webview above giving code is working but i want to pass dynamic value of url how i can pass the url value and where???
thanks in advance
I am tried to load url in webview above giving code is working but i want to pass dynamic value of url how i can pass the url value and where???
thanks in advance
how to Android fragment use : http://www.androidinterview.com/android-fragment-tutorial-example/
Excuse me,do you have the android studio version of project “FragmentTutorialWeb”? I am a new Androider and I want to run this project in android studio but failed. Thus I am here for your help.
Looking forward to your reply.
There’s the link at the end of the post. I add here again https://github.com/survivingwithandroid/Surviving-with-android/tree/master/FragmentTutorialWeb
Τhis site was… how do Ι say it? Relevant!! Finally
I’ve fߋund something that helpеd me. Thɑnks a lot!
Hi sir I want to build an app with 2 webviews using fragments,,how can I do that?Please give me some code for fragment class only it’s urgent
Thanks in advance
Sir I have created a project of navigation drawer activity on which I have used two fragments for webview, but only one webview is working and the second fragment shows totally white blank page,, please do give me some solutions
Thanks
I should have the code to answer