The New Features in Android Jetpack Fragment
By: Kayvan Kaseb
Tags: Android Apps, Android, Fragments, Android Jetpack, Android App Development

The picture is provided by Unsplash

As a matter of fact, a fragment is a reusable class that implements a portion of an activity in Android development. Fragments can be able to provide modularity and reusability into your activity’s UI by allowing you to separate the UI into discrete pieces. Furthermore, Android Jetpack has been announced by Google recently as a set of libraries, tools, and guidance for modern Android development. This article will discuss some advanced features and improvements in using fragments, which have been designed by Google for Android developers.

Introduction and Overview

As we know, fragments were introduced in Android Honeycomb API level 11 as micro activities. Basically, a fragment is a reusable class, which implements a portion of an activity. A Fragment usually defines a part of a user interface. Fragments have to be embedded in activities; they cannot run independently of activities. Therefore, fragments can be able to provide modularity and reusability into your activity’s UI by allowing you to divide the UI into discrete pieces. Activities are an ideal place to put global elements around your app’s user interface like navigation drawer. In contrast, fragments are appropriate for handling the UI of a single screen or portion of a screen. For instance, assume an Android app that reacts to different screen sizes. On larger screens, the Android app should display a static navigation drawer and a list in a grid layout. On smaller screens, the app should show a bottom navigation bar and a list in a linear layout. So, the question would be what is the best approach in this case? By utilizing fragments, the activity is responsible for showing the correct navigation UI while the fragment displays the list with the appropriate layout. In short, separating your UI into fragments makes it easier to modify your activity’s appearance at runtime. When your Android activity is in the STARTED lifecycle state or higher, fragments could be added, replaced, or removed. In fact, you can be able to maintain a history of these changes in a back stack, which is handled by the activity. One of the significant point in utilizing fragments in practice is that you can use multiple instances of the same fragment class in the same activity, in multiple activities, or even as a child of another fragment. This means you should just only support a fragment with the necessary logic to handle its own UI. In other words, you should avoid depending on or manipulating one fragment from another.

Additionally, Android Jetpack has been announced by Google recently as a set of libraries, tools, and guidance for modern Android development. Currently, there are four categories for using Jetpack, which includes: Architecture, UI, Behavior, and Foundation. It means that some Android Jetpack libraries, such as Navigation, BottomNavigationView , and ViewPager2 , are designed to work with fragments much more efficiently. As a result, in the following sections this essay will consider some new features and improvements in using fragments that have been implemented by Google for Android developers.

Sharing and communicating among fragments

To respond to user events properly, or to share state information, you typically require to have channels of communication between an activity and its fragments or between two or more fragments. To maintain fragments self-contained, you should not have fragments communicate directly with other fragments or with its host activity. The Fragment library offers two main choices for communication: a shared ViewModel and the Fragment Result API. It is obvious that the recommended approach is based on the situation. All in all, to share persistent data with any custom APIs, you should use a ViewModel. ViewModel objects can store and manage UI data. Also, for a one-time result with data that could be placed in a Bundle , you should use the Fragment Result API. Here, we will discuss the ViewModel approach that is provided by Android Jetpack. Assume a common case of split-view fragments, where you have a fragment in which the user chooses an item from a list and another fragment shows the contents of the selected item. In this example, the owner activity must bind the two together, and both fragments must manage the scenario where the other fragment is not yet created or visible as well. So, the code can be written as follows:

class SharedViewModel : ViewModel() {

val selected = MutableLiveData<Item>()



fun select(item: Item) {

selected.value = item

}

}



class MasterFragment : Fragment() {



private lateinit var itemSelector: Selector

private val model: SharedViewModel by activityViewModels()



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

itemSelector.setOnClickListener { item ->

// Updating the UI...

}

}

}



class DetailFragment : Fragment() { private val model: SharedViewModel by activityViewModels()



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->

// Updating the UI...

})

}

}

In fact, these fragments can share a ViewModel by using their activity scope to manage this communication. When the fragments each get the ViewModelProvider , they receive the same SharedViewModel instance that is scoped to this activity. There are some advantages can be mentioned in this approach. First of all, the activity does not require to know and react anything toward this communication at all. Second, if one of the fragments destroys, the another one keeps working as usual. Finally, each fragment has its own lifecycle. So, it is not affected by another one.

ViewModel and Navigation Graph

Initially, Android Jetpack’s Navigation component provides you to implement navigation, from simple button clicks to more complicated patterns, such as app bars and the navigation drawer. To be more specific, the Navigation component is a collection of libraries, a plug-in, and tooling, which simplifies Android navigation. In short, the Navigation component includes three key parts: Navigation graph, NavHost, and NavController. Navigation graph is an XML resource, which contains all navigation-related information in one centralized location. This includes all of the individual content areas within your app, called destinations. Thus, if you are using the Navigation library, you can scope a ViewModel to the lifecycle of a destination's NavBackStackEntry . For instance, the ViewModel could be scoped to the NavBackStackEntry for the ListFragment:

class ListFragment: Fragment() {

private val viewModel: ListViewModel by navGraphViewModels(R.id.list_fragment)



override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

viewModel.filteredList.observe(viewLifecycleOwner, Observer { item ->

// Updating the list UI

}

}

}

Fragment Manager and Navigation library

FragmentManager is the main class that is responsible for accomplishing tasks on your app's fragments, such as adding, removing, replacing, and adding them to the back stack. However, Google has recommended using the Navigation library to handle your Android app’s navigation as it was indicated. This framework offers some best practices for working effectively with fragments, the back stack, and the fragment manager. Probably, you will never face with FragmentManager directly if you are using the Jetpack Navigation library. Any Android app using fragments is using FragmentManager at some level; therefore, it would be vital to understand it completely and how it works for Android developers.

Testing fragments

Recently, Google has announced AndroidX Test. It brings testing as an essential part of Jetpack. Google include some of the existing libraries that you have used before, some new APIs, full Kotlin support, which allows you to write proper and concise tests. It is available on and off device. Furthermore, to set up the conditions for performing tests on fragments, the AndroidX library supports the FragmentScenario class to build fragments and change their Lifecycle.State . FragmentScenario has two main methods for launching fragments in tests as follows: 1. launchInContainer(): for testing a fragment's user interface. 2. launch(): for testing without the fragment's user interface. After selecting one of these options, you can assess information about its UI elements using Espresso UI tests. In general, to use FragmentScenario, you should define the fragment testing artifact in your Android app's build.gradle as follows:

dependencies {

def fragment_version = "1.2.5"



debugImplementation "androidx.fragment:fragment-testing:$fragment_version"

}

Additionally, if your fragments have some dependencies, you can be able tp provide test versions of these dependencies by providing a custom FragmentFactory to the launchInContainer() or launch() methods. The reason is that your fragment should not be dependent on a specific parent activity or fragment for testing. Also, you should not build a fragment’s view hierarchy unless the fragment is visible to the real user.

In your Android app’s UI tests, it is typically sufficient to launch the fragment under test, and start testing it from RESUMED state. However, you might also assess the fragment's behavior as it transitions from one lifecycle state to another one. To accomplish this goal, you should call moveToState() . This method supports other states.

FragmentContainerView

FragmentContainerView is a customized Layout, which has been designed for Fragments. It extends FrameLayout. So, it can manage Fragment Transactions, and it has additional features to coordinate with fragment behavior as well. In short, unlike other ViewGroups, it just only accepts Fragment Views. It also supports the <fragment> attributes, and offers more Fragment transactions flexibility.

<androidx.fragment.app.FragmentContainerView

xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

android:id="@+id/fragment_container_view"

android:layout_width="match_parent"

android:layout_height="match_parent">

</androidx.fragment.app.FragmentContainerView>

Besides, this container gave us the opportunity to address some animation issues, specifically an issue with z-ordering of Fragments. As a result, fragments using exit animations are drawn before all others for FragmentContainerView. This helps that exiting Fragments do not appear on top of the view.

Future works

Google has indicated that has some plans for improving fragments in various aspects. For example, they has suggested multiple back stack. This will allow saving and restoring a whole stack of fragments without losing state instead of having just single stack option. Additionally, they are panning to create a better API for startActivityForResult() and for passing a result to another fragment.

In conclusion

A Fragment usually defines a part of a user interface. Fragments must be embedded in activities; they cannot run independently of activities. Therefore, fragments can be able to provide modularity and reusability into your activity’s UI by allowing you to divide the UI into discrete pieces. This article discussed some new features and enhancements in using fragments for Android developers.