A fragment in the android framework

A fragment in the android framework

Introduction

fragment

A fragment is a modular part of an activity, which has its own life cycle, receives its own input events, you can add or remove it while the activity is running. A fragment unifies the View and logic so that it can be easily reused within one or more different activities. There can be more than one fragment in activities so that each fragment can represent a View within one activity.
The advantage of an architecture that uses fragments is that fragments allow us to reuse code, with a simple process of creating different views for tablets (landscape) and mobile devices.
Communication between two fragments is quite complicated and can be done in two ways:

Fragment vs. Activity

In applications that use fragments, a part of activity responsibilities is delegated to fragments, so according to this division, the responsibilities that remain in the activity would be as follows:

  • To contain navigation to other activities via an intent or navigation component (“NavigationDrawer”,“ViewPager”…)
  • To hide and show fragments (using fragment manager)
  • To receive data from other activities (intent)
  • To communicate with fragments and mediate communication between them

While fragments undertake to:

  • Show appropriate content
  • Event handling
  • Starting a network request
  • Retrieving and storing data

The life cycle of a fragment is directly influenced by the life cycle of an activity. When an activity is paused, then all fragments in it are also paused, and when an activity is destroyed, then all its fragments are also destroyed. However, while the activity is “live”, we can manipulate each fragment independently. You can view a detailed overview of lifecycle fragments and activities here.

fragment lifecycle

NOTE:
When implementing a fragment lifecycle method you should always call the superclass (eg super.onStart();):

Example

Creating fragments

Extending fragment class

Creating a fragment consists of creating the appropriate class responsible for the logic and adding the appropriate layout to it. The class responsible for the logic mustextends one of the following classes:

  • Fragment is the main class while the rest are its subclasses (see what the boilerplate code generated by android studio looks like).
  • DialogFragment – A fragment that displays a dialog window, floating on top of its activity window. This is typically used to display a warning dialog, a confirmation dialog, or to request information from the user in a frame without having to switch to another activity, allowing the user to return to the previous fragment.
  • PreferenceFragmentCompat is used to create a settings list for our application from where users have the ability to change the functionality and behavior of the application. (read more in the documentation).
  • ListFragment is used to display a list of some data and has an already implemented event listener on the click of a member from the list, so we only need to define the onListItemClick() method (primer).

Linking a fragment to its layout

To provide a layout for a fragment, you need to implement the “onCreateView()” method, which Android calls when it’s time for the fragment to draw its layout. The implementation of this method must return a view that is the root layout of your fragment. Connecting the fragment class and its view is done using the inflate() method as part of the onCreateView‘s lifecycle method:

See more about the process of inserting a view from xml into the “Layout inflation” class in the article “Converting XML resources to a View object”.

In addition to this method, we can create a fragment based on the Android template called Fragment(Blank), which includes a lot of prepared boilerplate code. See more about this here.

Targeting elements within the layout

In order to be able to use the findViewById() method within the fragment, we need to target its layout first. We can do it in two ways, depending on where we need it in the code:

  1. Within the onCreate() and onCreateView() methods, we first need to inflate the layout, after which we can use the findViewById() method:

    A fragment, unlike an activity, is not a subclass of the Context class, which means it doesn’t have access to global information about the application environment. This means that the fragment cannot use this to get the context. For this reason, as part of the onCreateView() method, the LayoutInflater object that has access to the context is passed as a parameter, so if we need a context, we can use it inflater.getContext().

  2. Within the onViewCreated() method, we target the fragment’s layout with the getView() method. This method is available when we extend our class with the Fragment class and can be called only afterview creation, and therefore we cannot use it inside the onCreate() or onCreateView() method.

Fragment embedding in activities

An activity containing a fragment must extend either FragmentActivity or its subclass AppCompatActivity. We can add fragments to the activity in two ways:

a) Static insertion of a fragment directly into the layout activity

Static insertion of a fragment into an activity implies that the fragment is inserted into the layout of the activity as a view.

The fragment inserted in this way must have a defined id in the XML, because it is targeted in the activity using the method findFragmentById():

When we have a reference to a fragment, we can call its public methods or properties.

b) Programmed insertion of fragments into the activity

The procedure for programmatically inserting a fragment into an activity is as follows:

1.) Creating fragment containers in XML

If your activity allows fragments to be removed and replaced, you should add the initial fragment (called fragmentContainer) to “onCreate()”. That container is used so that we can insert another fragment into it later.

2.) Creation of fragmentManager

Fragment manager is instantiated by calling method getSupportFragmentManager()

Fragment manager

Fragment manager is an object responsible for working with fragments for navigation between them as well as referencing fragments within the activity:

Method Description addOnBackStackChangedListener Adds a listener when the back stack changes (see here) beginTransaction() Creates a new transaction. findFragmentById(int id) Finds a fragment that is inflated directly into the XML layout activity. findFragmentByTag(String tag) Finds fragment via tag popBackStack() Removes a fragment from the backstack. executePendingTransactions() Forces transaction execution.
3.) Creating a fragment instance

Creating a fragment instance is by default using the new operator:

If we want to pass some data during instantiation, then they must be defined within the constructor method:

Then we pass them as a parameter:

It is recommended to use the so-called newInstance approach, when a factory method is defined within the fragment, which is used to instantiate the fragment:

Later in the fragment within the onCreate() method, we can request the data generated when the fragment is instantiated via the argument name:

The second argument in the getter is the default value in case it doesn’t find an argument.

4.) Performing some of the transactions with fragments (add, remove, replace)

There is an API for working with fragments in an activity (add, remove, or replace a fragment) and it is called FragmentTransaction. We get the FragmentTransaction instance using fragmentManager.

One of the methods that FragmentTransaction can perform is add(). This method serves to insert our fragment into a ViewGroup (container). That ViewGroup is defined through the first parameter, while the fragment we add is defined through the second parameter, and through the third parameter we define the TAG that will mark the added fragment (based on the tag, we can later target it with fragmentManager.findFragmentByTag(“TAGFRAGMENTA”);).

In order to return to the previous state by clicking the “back” button (that is, not to close the activity, which is the default), we need to add our fragment to the stack, the so-called “backStack” with the method addToBackStack().

After defining all the commands (there can be more than one), it is necessary to confirm the action (commit), after which they will actually be executed:

Actions that we have committed are not executed immediately, but are put on hold for execution on the main thread. Actions will be performed only when the thread is ready. See examples of transactions here.

Targeting fragment from activity

To target the fragment in the activity frame that was inserted in this way, the findFragmentByTag() method is used, which is passed the TAG parameter (defined through the third parameter of the replace() method).

Navigation between fragments

Navigation between fragments can be defined within the activity using only FragmentManager, however nothing prevents us from using one of the following approaches:

  • TabLayout (tabs, see more in the article)
  • Fragment Navigation Drawer (side menu see more in article)
  • ViewPager (slide switch between fragments, see more in article)

×

Creating from the preparatory boilerplate “Fragment(Blank)”

Factory static method newInstance() is used when instantiating a fragment within an activity and allows easy passing of parameters when instantiating a fragment:

Now within the fragment we can use the passed parameters using the variables mParam1 and mParam2 and the Fragment instantiation in the activity is done with the following code:

NOTE:
If a fragment is a subclass of ListFragment, it returns a ListView in the onCreateView() method by default, so there is no need to implement the part related to connecting the logic to the layout unless we use a custom layout.

As part of the boilerplate code comes a part related to CustomListener which are used to pass data from the fragment to the activity (another fragment).

See more about custom listeners in the article “Creating custom listeners”.

×

Example – ListFragment

×

Example

This example shows the definition of the initial fragment in the activity. In the empty container tag, we add a new view, our fragment.

Example

In this example, calling the showFragmentA method replaces the fragment

×

full fragment lifecycle

×

×

Back Stack

A stack is a type of memory in which elements are placed on top of each other, so that the last element added is on top (analogy to a heapplate). Elements are removed from the stack in reverse order, with the last element added being removed first. Within Android, there is a stack in which all activities are placed according to the order in which they are called. Pressing the “back” button deletes the last activity from the stack. However, in the case of using a fragment, this is not the default behavior, so when the user presses back from the stack, the last added fragment is not removed, but the entire activity. This is not the expected behavior and it is necessary for the developer to add this functionality “manually”.

backstack

addToBackStack

In order to insert the fragments on the backStack, they need to be added using the addToBackStack() method. This method needs to be added before every commit. The text that is passed is optional and is used if we later want to recognize that transaction, however it is perfectly fine to pass null as a parameter.

FragmentManager.OnBackStackChangedListener

If the application with the change of fragments needs to update other elements of the user interface (eg actionBar) it means that it should react after the change of backStack. In that case, it is necessary to use the interface addOnBackStackChangedListener and to define the callback method onBackStackChanged() which should perform an action after triggering the event

Example when an activity implements an interface

Example when the callback method onBackStackChanged() is defined “on the fly”: