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"