All Articles

AndroidDevSummit 2019: What's new in Android Fragments

What’s New

One thing that caught my attention in AndroidDevSummit 2019 was the release of androidx.fragment 1.2.0. It’s currently a release candidate and this release includes some new APIs to make it easier to deal with fragments.

In this blog, we are going to look at a couple of new additions in this release. FragmentContainerView is a new view that should be used for hosting fragments. It replaces the usual FrameLayout that was usually used for this kind of thing. There is also FragmentFactory which is a new way to customise how fragments are created.

FragmentContainerView vs FrameLayout

One of the common patterns to host fragments in an activity is to use a FrameLayout. The Android community have been doing this for years but this is going to change now. The androidx team introduced this new view called FragmentContainerView to do this for you.

<androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

All you have to do is replace your FrameLayout with FragmentContainerView and everything should work out of the box, you won’t need to change anything on the way you handle fragment transactions.

The benefits gained here is the improved handling of what’s called the z-ordering for fragments. Here’s the example they used, but basically this means , for example, that the exit and enter transitions between two fragments will not overlap each other. Instead this FragmentContainerView is going to start the exit animation first then the enter animation.

If you are asking can this replace <fragment> tag? then the answer is yes. All you have to do is to add android:name="your_class_name" when defining FragmentContainerView and it will use a FragmentTransaction under the hood to build and show your fragment. This mean you can use fragment transactions later on to replace it.

<androidx.fragment.app.FragmentContainerView
        android:id="@+id/fragment_container"
        android:name="com.company.FragmentClass"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

FragmentFactory

The most common advice for creating a Fragment was always have a no-argument constructor for your fragments. Up until now, the Android framework needed this no-argument constructor to initialise your fragment. FragmentFactory changes this. It gives you control on how to build Fragments which means you can do constructor dependency injection for you fragments moving forward.

All you need to do is sub-class FragmentFactory and provide you custom implementation, then tell the fragment manager to use this factory when building fragments.

// Your custom fragment factory
class MyFragmentFactory (
    private val exampleService: ExampleService
) : FragmentFactory() {

    override fun instantiate(classLoader: ClassLoader, className: String): Fragment { ... }

}

// Your Activity
class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var fragmentFactory: MyFragmentFactory

    override fun onCreate(savedInstanceState: Bundle?) {
        supportFragmentManager.fragmentFactory = fragmentFactory
        super.onCreate(savedInstanceState)
        ...
    }
}

Also, The Android community was used to create an instance of the fragment then pass it to the fragment transaction.

supportFragmentManager
            .beginTransaction()
            .replace(R.id.fragment_container, SecondFragment())
            .commit()

In this new release, the FragmentTransaction has some new overloads to allow you to pass Class<Fragment> which is then passed to FragmentFactory to take care of creating an instance of the fragment. So the code example above becomes like this.

supportFragmentManager.beginTransaction()
                .replace(R.id.fragment_container, SecondFragment::class.java, args = null)
                .commit()

Testing Fragments with FragmentScenario

We all have fragments which rely on Dagger to get their dependencies. Now with FragmentFactory we can easily replace this Dagger injection with constructor injection. This also makes testing those fragments easier as we don’t have to worry about injecting mocks via dagger.

androidx.fragment:fragment-testing also is getting update to allow you provide factories to build the fragment under test. This means that you can have a mock factory which will use constructor injection to inject mock dependencies in the fragment. It’s that simple!

val scenario = launchFragmentInContainer<FirstFragment>(factory = MockFactory())
        // Your test action
        scenario.onFragment { fragment ->
            // Your test assertions

        }

Summary

The new androidx.fragment release is a big step to simplify using fragments. The ease of setup and testing now is a welcome change at least for me and I hope you find it useful too.

For the full breakdown on all the changes included in this release head to the official release note page.

If you want test all this out then add this to your build.gradle

  def fragment_version = "1.2.0-rc01"
  implementation "androidx.fragment:fragment-ktx:$fragment_version"
  implementation "androidx.fragment:fragment-testing:$fragment_version"