NOW Hiring

Quick links

This tutorial describes how to use the Fragment class to create scalable and flexible Android applications.

1. Fragments

1.1. What are single-pane or multi-pane layouts?

Android devices exists in a variety of screen sizes and densities.

A panel or pane represents a part of the user interface. The term pane is a general term used to describe the concept that multiple views are combined into one compound view depending on the actual available space.

Panel

If not enough space is available only one panel is shown. This is typically called a single-pane layout.

Panel

If more space is available, multiple panels can be shown.

Panel

1.2. What are fragments?

A fragment is an independent Android component which can be used by an activity. A fragment encapsulates functionality so that it is easier to reuse within activities and layouts.

A fragment runs in the context of an activity, but has its own life cycle and typically its own user interface. It is also possible to define fragments without an user interface, i.e., headless fragments.

1.3. Fragments and Context access

Fragments don’t subclass the Context class. Therefore you have to use the getActivity() method to get the parent activity.

1.4. Advantages of using fragments

Fragments simplify the reuse of components in different layouts and their logic. You can easily build single-pane layouts for handsets (phones) and multi-pane layouts for tablets. You can also use fragments also to support different layout for landscape and portrait orientation on a smartphone.

As it is possible to dynamically add and remove fragments from an activity. The usage of fragments allows to design very flexible user interfaces.

The typical example is a list of items in an activity. On a tablet you see the details immediately on the same screen on the right hand side if you click on item. On a smartphone you jump to a new detail screen. This is depicted in the following graphic.

Fragments usage on a handheld device

The following discussion will assume that you have two fragments (main and detail), but you can also have more. We will also have one main activity and one detailed activity. On a tablet the main activity contains both fragments in its layout, on a handheld it only contains the main fragment.

The following screenshots demonstrates this usage.

Fragments usage on a tablet device

1.5. How to support different screensizes with fragments

It is possible to define in the layout file of an activity that it contains fragments (static definition). You can also modify the fragments of an activity at runtime (dynamic definition).

To display different fragments in your activities based on the actual available space you can:

  • Use one activity, which displays two fragments for tablets and on handset devices. In this case change at runtime the fragments displayed by the activity whenever necessary. In this scenario you typically define instances of the FrameLayout class as placeholder in your layout and add the fragments at runtime to them.

  • Use separate activities to host each fragment on a handset. For example, when the tablet UI uses two fragments in an activity, use the same activity for handsets, but supply an alternative layout that includes just one fragment. If the detailed fragment is there, the main activity tells the fragment that it should update itself. If the detail fragment is not available, the main activity starts the detailed activity.

Which option to select depends on the use case, typical the dynamic contribution is more flexible bit a bit harder to implement.

2. Defining and using fragments

2.1. Defining fragments

To define a new fragment you either extend the android.app.Fragment class or one of its subclasses. Subclasses are for example, ListFragment, DialogFragment, PreferenceFragment or WebViewFragment. The following code shows an example implementation.

package com.example.android.rssreader;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class DetailFragment extends Fragment {
    public static final String EXTRA_URL ="url";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_rssitem_detail,
                container, false);
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Bundle bundle = getArguments();
        if (bundle != null) {
            String link = bundle.getString("url");
            setText(link);
        }
    }

        public void setText(String url) {
                TextView view = (TextView) getView().findViewById(R.id.detailsText);
                view.setText(url);
        }
}

2.2. Application communication with fragments

To increase reuse of fragments, they should not directly communicate with each other. Every communication of the fragments should be done via the host activity.

For this purpose a fragment should define an interface as an inner type. The fragment requires that the activity, which uses it, must implement this interface. This way you avoid that the fragment has any knowledge about the activity which uses it. In its onAttach() method it can check if the activity correctly implements this interface.

For example, assume you have a fragment which should communicate a value to its parent activity. This can be implemented like the following.

package com.example.android.rssreader;

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class MyListFragment extends Fragment {

        private OnItemSelectedListener listener;

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                        Bundle savedInstanceState) {
                View view = inflater.inflate(R.layout.fragment_rsslist_overview,
                                container, false);
                Button button = (Button) view.findViewById(R.id.button1);
                button.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                                updateDetail("fake");
                        }
                });
                return view;
        }

        public interface OnItemSelectedListener {
                public void onRssItemSelected(String link);
        }

        @Override
        public void onAttach(Context context) {
                super.onAttach(context);
                if (context instanceof OnItemSelectedListener) {
                        listener = (OnItemSelectedListener) context;
                } else {
                        throw new ClassCastException(context.toString()
                                        + " must implemenet MyListFragment.OnItemSelectedListener");
                }
        }

        @Override
        public void onDetach() {
                super.onDetach();
                listener = null;
        }

        // may also be triggered from the Activity
        public void updateDetail(String uri) {
                // create a string just for testing
                String newTime = String.valueOf(System.currentTimeMillis());

                // inform the Activity about the change based
                // interface defintion
                listener.onRssItemSelected(newTime);
        }
}

2.3. Passing parameters to fragments

An activity can pass a bundle to the fragment.

detailFragment = new DetailFragment();

// configure link
Bundle bundle = new Bundle();
bundle.putString("link", link);
detailFragment.setArguments(bundle);

The fragment gets this in its onActivityCreated method.

@Override
public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Bundle bundle = getArguments();
        if (bundle != null) {
                setText(bundle.getString("link"));
        }
}

3. Fragment life-cycle

A fragment has its own life cycle. But it is always connected to the life cycle of the activity which uses the fragment.

Fragment lifecycle

If an activity stops, its fragments are also stopped. If an activity is destroyed, its fragments are also destroyed.

Table Title

Method Description

onAttach()

The fragment instance is associated with an activity instance.The fragment and the activity is not fully initialized. Typically you get in this method a reference to the activity which uses the fragment for further initialization work.

onCreate()

Fragment is created. The onCreate() method is called after the onCreate() method of the activity but before the onCreateView() method of the fragment.

onCreateView()

The fragment instance creates its view hierarchy. In the onCreateView() method the fragment creates its user interface. Here you can inflate a layout via the inflate() method call of the Inflator object passed as a parameter to this method.

In this method you should not interactive with the activity, the activity is not yet fully initialized.

There is no need to implement this method for headless fragments .The inflated views become part of the view hierarchy of its containing activity.

onActivityCreated()

The onActivityCreated() is called after the onCreateView() method when the host activity is created.

Activity and fragment instance have been created as well as the view hierarchy of the activity. At this point, view can be accessed with the findViewById() method. example.

In this method you can instantiate objects which require a Context object.

onStart()

The onStart() method is called once the fragment gets visible.

onResume()

Fragment becomes active.

onPause()

Fragment is visible but becomes not active anymore, e.g., if another activity is animating on top of the activity which contains the fragment.

onStop()

Fragment becomes not visible.

onDestroyView()

Destroys the view of the fragment. If the fragment is recreated from the backstack this method is called and afterwards the onCreateView method.

onDestroy()

Not guaranteed to be called by the Android platform.

4. Defining fragments for your activity

4.1. Adding fragments statically to the layout file

To use your new fragment, you can statically add it to an XML layout. In this case the android:name attribute points to the corresponding class as demonstrated by the following code snippet.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:baselineAligned="false"
    android:orientation="horizontal" >

    <fragment
        android:id="@+id/listFragment"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        class="com.example.android.rssreader.MyListFragment"
        tools:layout="@layout/fragment_rsslist_overview">
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:layout_width="0dp"
        android:layout_weight="2"
        android:layout_height="match_parent"
        class="com.example.android.rssreader.DetailFragment"
        tools:layout="@layout/fragment_rssitem_detail">
    </fragment>

</LinearLayout>

Using this scenario makes sense in case you have different static layout files for different device configurations.

4.2. Handling dynamics in fragments

The FragmentManager class allows you to add, remove and replace fragments in the layout of your activity. It can accessed in an activity via the getFragmentManager() method.

The modifications must be performed in a transaction via the FragmentTransaction class.

To modify the fragments in an activity you typically define a FrameLayout placeholder in the layout file.

<?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="horizontal" >

    <FrameLayout
        android:id="@+id/listcontainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <FrameLayout
        android:id="@+id/detailscontainer"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="gone" />

</LinearLayout>

You use the FragmentManager to replace the container with a fragment.

// get fragment manager
FragmentManager fm = getFragmentManager();

// add
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.your_placehodler, new YourFragment());
// alternatively add it with a tag
// trx.add(R.id.your_placehodler, new YourFragment(), "detail");
ft.commit();

// replace
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.your_placehodler, new YourFragment());
ft.commit();

// remove
Fragment fragment = fm.findFragmentById(R.id.your_placehodler);
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fragment);
ft.commit();

A new fragment replaces an existing fragment in this container.

If you want to add the transaction to the backstack of Android, you use the addToBackStack() method. This adds the action to the history stack of the activity and allows the user to revert this change via the back button.

4.3. Check if a fragment is present in the layout

To check if a fragment is part of your layout you can use the FragmentManager class. The isInLayout() method works on if the fragment as added to the activity via its layout.

DetailFragment fragment = (DetailFragment) getFragmentManager().
        findFragmentById(R.id.detail_frag);
if (fragment==null || ! fragment.isInLayout()) {
        // start new Activity
        }
else {
        fragment.update(...);
}

4.4. Determine how many fragments are present

As the logic in the activity depends on the scenario (single/multi pane), you typically write a check to determine in which mode you are. There are several approaches to perform this. One way is to define a configuration file in the values resource folder. The key / value pair defaults to false and a additional configuration file set this value to true for the desired screensize.

For example this is a possible default config.xml configuration file.

Unresolved directive in 018_dynamicfragments.adoc - include::res/mode/config.xml[]

In the values-land folder you would place a config.xml configuration file with a different value.

Unresolved directive in 018_dynamicfragments.adoc - include::res/mode/config2.xml[]

In your code you can access the state via the following snippet.

getResources().getBoolean(R.bool.twoPaneMode);

5. Adding fragments transition to the backstack

You can add a FragmentTransition to the backstack to allow the user to use the back button to reverse the transition.

For this you can use the addToBackStack() method on the FragmentTransition object.

6. Animations for fragment transitions

During a fragment transaction, you can define animations based on the Property Animation API via the setCustomAnimations() method.

You can also use several standard Android animations via the setTransition() method call. These are defined via the constants starting with FragmentTransaction.TRANSIT_FRAGMENT_*.

Both methods allow you to define an entry animation and an existing animation.

7. Persisting data in fragments

7.1. Persisting data between application restarts

Frequently in your Android application you need to persist your application data. For this, you can for example use an SQLite database or a file.

7.2. Persisting data between configurations changes

If you want to persist data between configuration changes, you can also use the application object.

In addition to that you can use the setRetainState(true) method call on the fragment. This retains the fragment instances between configuration changes. It only works if the fragments are not added to the backstack. In this case the data must be stored as member (field).

Using this method is not recommended by Google for fragments which have a user interface. See [headlessfragments1].

You can use the onSaveInstanceState() method to place the data in the Bundle. You can retrieve that data the onActivityCreated() method.

8. Fragments for background processing

8.1. Headless Fragments

Fragments can be used without defining a user interface. These are called headless fragments. To implement such a fragment, return null in the onCreateView() method of your fragment.

It is recommended to use services for background processing. If you want to do this via your fragments, the second best solution is to use headless fragments in combination with the setRetainInstance() method. This way you don’t have to handle the configuration changes during your asynchronous processing yourself.

8.2. Retained headless fragments to handle configuration changes

Headless fragment are typically used to encapsulate some state across configuration changes or for a background processing task. For this purpose you would set your headless fragment to be retained. A retained fragment is not destroyed during configuration changes.

Retained headless fragment

To set your fragment to retained, call its setRetainInstance() method.

To add such a fragment to an activity you use the add() method of the FragmentManager class. If you need to refer to this Fragment later, you need to add it with a tag. This allows you to search for it via the findFragmentByTag() method of the FragmentManager.

The usage of the onRetainNonConfigurationInstance() in the activity is deprecated and should be replaced by retained headless fragments.

9. Exercise: Using fragments

9.1. Target of this exercise

The following tutorial demonstrates how to use fragments in a standard Android application without using the support library. The application uses layouts with different fragments depending on portrait and landscape mode.

In portrait mode the RssfeedActivity shows one fragment. From this fragment the user can navigate to another activity which contains another fragment.

In landscape mode RssfeedActivity shows both fragments side by side.

9.2. Create Project

Create a new Android project with the following data. Do NOT use the compability layer.

Table Android project

Property Value

Application Name

RSS Reader

Company Domain

android.example.com

Package name

com.example.android.rssreader

Template

Empty Activity

Activity

RssfeedActivity

Layout

activity_rssfeed

9.3. Create layout files for the fragments

Create a new layout file called fragment_rssitem_detail.xml in the res/layout/ folder.

<?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/detailsText"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center_horizontal|center_vertical"
        android:layout_marginTop="20dip"
        android:text="Default Text"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="30dip" />

</LinearLayout>
The id must be correct, the fragment uses this to search for the TextView.

Create a new layout file called fragment_rsslist_overview.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" >

    <Button
        android:id="@+id/updateButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Press to update"
         />

</LinearLayout>
Also here, the id must be correct.

9.4. Create fragment classes

Create the follows classes which are used as fragments. Start with the DetailFragment class.

package com.example.android.rssreader;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class DetailFragment extends Fragment {
    public static final String EXTRA_URL ="url";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_rssitem_detail,
                container, false);
        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        Bundle bundle = getArguments();
        if (bundle != null) {
            String link = bundle.getString("url");
            setText(link);
        }
    }

        public void setText(String url) {
                TextView view = (TextView) getView().findViewById(R.id.detailsText);
                view.setText(url);
        }
}

Create the MyListFragment class. Despite its name it will not display a list of items. Instead it will just have a button which allow to send the current time to the details fragment.

package com.example.android.rssreader;

import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class MyListFragment extends Fragment {

    private OnItemSelectedListener listener;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_rsslist_overview,
                container, false);
        Button button = (Button) view.findViewById(R.id.updateButton);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                updateDetail("fake");
            }
        });
        return view;
    }

    public interface OnItemSelectedListener {
        void onRssItemSelected(String link);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnItemSelectedListener) {
            listener = (OnItemSelectedListener) context;
        } else {
            throw new ClassCastException(context.toString()
                    + " must implement MyListFragment.OnItemSelectedListener");
        }
    }

    // triggers update of the details fragment
    public void updateDetail(String uri) {
        // create fake data
        String newTime = String.valueOf(System.currentTimeMillis());
        // send data to activity
        listener.onRssItemSelected(newTime);
    }
}

9.5. Change the main layout file

Change the existing activity_rssfeed.xml file.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:baselineAligned="false"
    android:orientation="horizontal" >

    <fragment
        android:id="@+id/listFragment"
        android:layout_width="0dp"
        android:layout_weight="1"
        android:layout_height="match_parent"
        class="com.example.android.rssreader.MyListFragment"
        tools:layout="@layout/fragment_rsslist_overview">
    </fragment>
    <fragment
        android:id="@+id/detailFragment"
        android:layout_width="0dp"
        android:layout_weight="2"
        android:layout_height="match_parent"
        class="com.example.android.rssreader.DetailFragment"
        tools:layout="@layout/fragment_rssitem_detail">
    </fragment>

</LinearLayout>

9.6. RssfeedActivity

Change the RssfeedActivity class so that it can act as call back for the list fragment and update the detailed fragment.

package com.example.android.rssreader;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class RssfeedActivity extends Activity implements MyListFragment.OnItemSelectedListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rssfeed);
    }

    @Override
    public void onRssItemSelected(String link) {
        DetailFragment fragment = (DetailFragment) getFragmentManager()
                .findFragmentById(R.id.detailFragment);
        fragment.setText(link);
    }

}
The tools: tag is optional. It points to a layout to that the preview in Android Studio can display the fragment.

9.7. Verify that the application works as expected

Start your application. Both fragments should be displayed both in landscape and portrait mode. You can use the emulator controls to change the orientation of the emulator. If you press the button in the ListFragment, the DetailFragment gets updated.

rssfeedfirstversionresult

10. Exercise: Use different number of fragments depending on the configuration

10.1. Target of this exercise

The RssfeedActivity should show two fragments (two panes) if started in landscape mode. In this exercise you adjust your application to support this.

10.2. Create activity layout for portrait mode

Create the following activity_rssfeed.xml layout file based on the port resource qualifier for orientation.

<?xml version="1.0" encoding="utf-8"?>
<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"
    tools:context=".RssfeedActivity"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:orientation="horizontal"
        android:layout_weight="1"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        >

    </FrameLayout>

</LinearLayout>

10.3. Define a boolean flag dependent of the resource selector

Create a file in your res/values folder called config.xml with the following setting.

<resources>
    <item type="bool" name="twoPaneMode">false</item>
</resources>

Create the same file in your res/values-land folder with a different value.

<resources>
    <item type="bool" name="twoPaneMode">true</item>
</resources>

10.4. Adjust the RssfeedActivity activity

Adjust the RssfeedActivity class to replace the existing fragment in case you are in single pane mode.

package com.example.android.rssreader;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.FrameLayout;

public class RssfeedActivity extends Activity implements
        MyListFragment.OnItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rssfeed);
        if (getResources().getBoolean(R.bool.twoPaneMode)) {
            // all good, we use the fragments defined in the layout
            return;
        }
        // if savedInstanceState is null we do some cleanup
        if (savedInstanceState != null) {
            // cleanup any existing fragments in case we are in detailed mode (1)
            getFragmentManager().executePendingTransactions();
            Fragment fragmentById = getFragmentManager().
                    findFragmentById(R.id.fragment_container);
            if (fragmentById!=null) {
                getFragmentManager().beginTransaction()
                        .remove(fragmentById).commit();
            }
        }
        MyListFragment listFragment = new MyListFragment();
        FrameLayout viewById = (FrameLayout) findViewById(R.id.fragment_container);
        getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, listFragment).commit();
    }

    @Override
    public void onRssItemSelected(String link) {
        if (getResources().getBoolean(R.bool.twoPaneMode)) {
            DetailFragment fragment = (DetailFragment) getFragmentManager()
                    .findFragmentById(R.id.detailFragment);
            fragment.setText(link);
        } else {
            // replace the fragment
            // Create fragment and give it an argument for the selected article
            DetailFragment newFragment = new DetailFragment();
            Bundle args = new Bundle();
            args.putString(DetailFragment.EXTRA_URL, link);
            newFragment.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, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();

        }
    }


}
1 This cleanup is necessary because we are using dynamically switching between two fragments and one fragments. That is an unusual scenario, typically the selection of one or two fragment is based on the smallest width of a device and does not change at runtime. The FragmentManager caches the fragment to optimize performance, so we need to ensure that we remove an existing detailed fragment.

10.5. Validate your implementation

Test your application. If you run the application in portrait mode, you should see only one fragment. In horizontal mode you see both fragments.

Switch the orientation of the emulator. Press the button in portrait as well as in horizontal mode and verify that the detail activity shows the current time.

Screenshot

11. Exercise: Use a headless retained fragment to store the state

11.1. Target of this exercise

We would like to remember the last selection. We use a headless fragment for this

11.2. Create headless fragment

package com.example.android.rssreader;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class SelectionStateFragment extends Fragment {
    public String lastSelection = "";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return null;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}

11.3. Use headless fragment to store the last selected value

package com.example.android.rssreader;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.FrameLayout;

public class RssfeedActivity extends Activity implements
        MyListFragment.OnItemSelectedListener {

    SelectionStateFragment stateFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rssfeed);
        stateFragment =
                (SelectionStateFragment) getFragmentManager()
                        .findFragmentByTag("headless");

        if(stateFragment == null) {
            stateFragment = new SelectionStateFragment();
            getFragmentManager().beginTransaction()
                    .add(stateFragment, "headless").commit();
        }

        if (findViewById(R.id.fragment_container) == null) {
            // restore state
            if (stateFragment.lastSelection.length()>0) {
                onRssItemSelected(stateFragment.lastSelection);
            }
            // all good, we use the fragments defined in the layout
            return;
        }
        // if savedInstanceState is null we do some cleanup
        if (savedInstanceState != null) {
            // cleanup any existing fragments in case we are in detailed mode (1)
            getFragmentManager().executePendingTransactions();
            Fragment fragmentById = getFragmentManager().
                    findFragmentById(R.id.fragment_container);
            if (fragmentById!=null) {
                getFragmentManager().beginTransaction()
                        .remove(fragmentById).commit();
            }
        }

        MyListFragment listFragment = new MyListFragment();
        FrameLayout viewById = (FrameLayout) findViewById(R.id.fragment_container);
        getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, listFragment).commit();

    }

    @Override
    public void onRssItemSelected(String link) {
        stateFragment.lastSelection = link;
        if (getResources().getBoolean(R.bool.twoPaneMode)) {
            DetailFragment fragment = (DetailFragment) getFragmentManager()
                    .findFragmentById(R.id.detailFragment);
            fragment.setText(link);

        } else {
            // replace the fragment
            // Create fragment and give it an argument for the selected article
            DetailFragment newFragment = new DetailFragment();
            Bundle args = new Bundle();
            args.putString(DetailFragment.EXTRA_URL, link);
            newFragment.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, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();

        }
    }


}

11.4. Define a boolean flag dependent of the resource selector

Create a file in your res/values folder called config.xml with the following setting.

Unresolved directive in 064_fragments_tutorialpart_headless.adoc - include::res/mode/config.xml[]

Create the same file in your res/values-land folder with a different value.

Unresolved directive in 064_fragments_tutorialpart_headless.adoc - include::res/mode/config2.xml[]

11.5. Adjust the RssfeedActivity activity

Adjust the RssfeedActivity class to replace the existing fragment in case you are in single pane mode.

package com.example.android.rssreader;

import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.widget.FrameLayout;

public class RssfeedActivity extends Activity implements
        MyListFragment.OnItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_rssfeed);
        if (getResources().getBoolean(R.bool.twoPaneMode)) {
            // all good, we use the fragments defined in the layout
            return;
        }
        // if savedInstanceState is null we do some cleanup
        if (savedInstanceState != null) {
            // cleanup any existing fragments in case we are in detailed mode (1)
            getFragmentManager().executePendingTransactions();
            Fragment fragmentById = getFragmentManager().
                    findFragmentById(R.id.fragment_container);
            if (fragmentById!=null) {
                getFragmentManager().beginTransaction()
                        .remove(fragmentById).commit();
            }
        }
        MyListFragment listFragment = new MyListFragment();
        FrameLayout viewById = (FrameLayout) findViewById(R.id.fragment_container);
        getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, listFragment).commit();
    }

    @Override
    public void onRssItemSelected(String link) {
        if (getResources().getBoolean(R.bool.twoPaneMode)) {
            DetailFragment fragment = (DetailFragment) getFragmentManager()
                    .findFragmentById(R.id.detailFragment);
            fragment.setText(link);
        } else {
            // replace the fragment
            // Create fragment and give it an argument for the selected article
            DetailFragment newFragment = new DetailFragment();
            Bundle args = new Bundle();
            args.putString(DetailFragment.EXTRA_URL, link);
            newFragment.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, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();

        }
    }


}
1 This cleanup is necessary because we are using dynamically switching between two fragments and one fragments. That is an unusual scenario, typically the selection of one or two fragment is based on the smallest width of a device and does not change at runtime. The FragmentManager caches the fragment to optimize performance, so we need to ensure that we remove an existing detailed fragment.

11.6. Validate your implementation

Test your application. If you run the application in portrait mode, you should see only one fragment. In horizontal mode you see both fragments.

Switch the orientation of the emulator. Press the button in portrait as well as in horizontal mode and verify that the detail activity shows the current time.

Screenshot

12. About this website

12.1. Android Fragment resources

Not yet listed.

12.2. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.