What Are Fragments in Android?

Fragments

Fragments are great for providing your app with UI flexibility. Suppose we have a simple app which shows a list of items. When clicking on an item in the list, we would like to present the user with more details about said item. This diagram shows how we would like our app to behave:

As a beginner, you may assume that the best way to go about doing this would be to use two activities. One for showing the list and another for showing details when the user clicks on an item. While this solution works, it is not flexible in any sense of the word. What happens when we decide to display this information on a larger device? If we were to restrict ourselves to only using activities, each Activity UI would take up the entire screen. This is not a good use of our available screen space. Doing this would make our app would feel barebones and empty. Luckily for us, there is a way to show multiple (or single) UIs in our Activity.

This is where Fragments come in to play. A Fragment in Android is essentially an individual part, or portion, of a UI. Building and combining multiple fragments allows us to compose them in an Activity to create multi-pane user interfaces. Fragments are essential when it comes to following the master/detail design concept, as shown in the diagram above. Like Activities, Fragments have their own layouts and life cycle methods. However, unlike Activities, the Android OS does not have any knowledge of an Activity’s Fragment. Its life cycle methods are called and managed by the Activity which hosts the Fragment(s).

Creating our Fragment class

First, let’s take a look as to how we actually make a Fragment class. For starters, we have to manually inflate the Fragment’s layout ourselves. There is a life cycle method which helps us with that, called onCreateView() .

public class SimpleFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_simple, container, false);
        Button b = view.findViewById(R.id.b_show_toast);
        b.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(), "Hello!", Toast.LENGTH_SHORT).show();
            }
        });
        return view;
    }
}

onCreateView() needs to return an instance of our layout (which is the View we return), so we inflate and return it. This is also the method where we get references to our widgets and hook them up with listeners. Unlike in an Activity, we do not get a reference to our widgets by calling findViewById() directly – we do so on our view. There are more life cycle methods unique to Fragments, but for our basic use case this is all the code we will need.

Adding Fragments to our Activity’s Layout

But how do we go about adding a Fragment to our Activity? It is actually quite simple. We have two different approaches we can take when it comes to adding a Fragment.

  1. We add our Fragment to the Activity’s layout
  2. We add our Fragment to the Activity’s code

The first method is relatively straightforward. All we need to do is define our Fragment in our Activity’s layout, as shown below.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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="com.moducode.a02allaboutfragments.MainActivity">

    <fragment
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="com.moducode.a02allaboutfragments.SimpleFragment">
    </fragment>

</android.support.constraint.ConstraintLayout>

It requires a very little amount of work if we want to include a Fragment in our layout. We are not even required to write any code in our Activity to get our Fragment functioning. The only thing we need to make sure of is that we reference the Fragment through the name attribute. The ease of this method comes at a steep cost – we now have very little control over our Fragment. We are essentially hard-coding our Fragment to our Activity. We will be unable to make any changes at run-time, such as swapping out the Fragment for another.

Adding Fragments to our Activity’s code

The second method is a bit more complex, but offers us much more control over our Fragment. Instead of explicitly defining a Fragment  in our XML, we will use a FrameLayout which will act as a container for our Fragment. Our Activity’s layout will now look like the following.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    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="com.moducode.a02allaboutfragments.MainActivity">

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

</android.support.constraint.ConstraintLayout>

This layout looks very similar to the one above. You can see we are now using a FrameLayout instead of Fragment explicitly. We have also removed the name attribute as we will now be telling our Activity which Fragment to use through code, instead of XML. Below is the code which will allow us to do that.

public class MainActivity extends AppCompatActivity {

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

        FragmentManager manager = getSupportFragmentManager();
        Fragment simpleFragment = manager.findFragmentById(R.id.fragment_container);

        if (simpleFragment == null) {
            simpleFragment = new SimpleFragment();
            manager.beginTransaction()
                    .replace(R.id.fragment_container, simpleFragment)
                    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
                    .commit();
        }
    }
}

Breaking down the code

The above code may look a little complex, but we will break it down step by step. First of all – adding, removing, detaching or replacing fragments in our Activity is all done through what is known as the FragmentManager. The FragmentManager is responsible for managing our fragments as well as maintaining a list of fragments and fragment transactions.

We begin by getting a reference to the Activity’s FragmentManager by calling getSupportFragmentManager(). We then get a reference to our Fragment by calling findFragmentById() on our FragmentManager. This method requires the id we used in our FrameLayout. It can be either the id of the container view (our FrameLayout in this case) or the id of a fragment explicitly (if we were taking the layout approach). We start by assigning our simpleFragment to that of a new SimpleFragment(). The code below this line is the most interesting – this is what is known as a Fragment transaction.

The beginTransaction() method returns a FragmentTransaction itself which allows us to chain further method calls. replace() will remove any current Fragment and replace it with the one we specify in its arguments. setTransition() allows us to set an animation for when we add (or remove) our Fragment (having transitions is not required to make the transaction work). Finally, we commit the transaction by calling commit().

We check if our Fragment is null just in case it is not in our FragmentManager ’s list. The FragmentManager maintains its list of Fragments across a device reconfiguration. So, if our Activity is recreated, it will show our Fragment without ever hitting our null check. However, if the Activity is created for the first time, simpleFragment will indeed be null and the transaction will be carried out.

Liked the article? Share it!

Leave a Reply

avatar

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  Subscribe  
Notify of