本文译自AndroidDevSummit 2019: What's new in Android Fragments,介绍了Fragment 1.2版本中引入的FragmentContainerView和FragmentFactory的使用。
本文首发:http://yuweiguocn.github.io/
《过零丁洋》
辛苦遭逢起一经,干戈寥落四周星。
山河破碎风飘絮,身世浮沉雨打萍。
惶恐滩头说惶恐,零丁洋里叹零丁。
人生自古谁无死?留取丹心照汗青。
-宋代,文天祥
新特性
在2019 AndroidDevSummit上我注意到的一件事情是发布了androidx.fragment:1.2.0-rc01
。这个是当前release版本的候选,这个版本包含一些更容易处理fragment的新的API。
在本文中,我们来学习一下这个版本中新添加的两个API。FragmentContainerView
是一个应该被用于fragment的宿主的新View,它替换了通常使用的FrameLayout
。还有一个FragmentFactory
用于自定义fragment如何被创建。
FragmentContainerView vs FrameLayout
在activity中使用FrameLayout
作为fragment的宿主是一个很常见的模板。在Android社区中已经这么做了很多年但这马上将会改变。androidx团队引入了名为FragmentContainerView
的View处理这个事情。
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
你所需要做的就是使用FragmentContainerView
替换FrameLayout
并且一切都会开箱即用,对于你处理的fragment transactions什么都不需要修改。译者注:FragmentContainerView继承自FrameLayout,但只允许添加fragment view。
它带来的好处是改进了对fragment z-ordering的处理。这个是他们使用是例子,这意味着两个fragment之前的退出和进入过渡不会互相重叠。使用FragmentContainerView
将先开启退出动画然后才是进入动画。
如果你问这个能否代替<fragment>
标签?答案是可以的。你需要做的是添加android:name="your_class_name"
,它会使用FragmentTransaction
构建显示你的fragment。这意味着稍候你可以使用fragment transactions替换它。
<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
对于创建一个Fragment
最常见的建议是让你的fragment保留一个无参的构造方法。直到现在,Android框架需要这个无参构造方法实例化你的fragment。FragmentFactory
的到来改变了这一现状。它可以让你控制怎样构建fragment,这意味着你可以在fragment中使用构造函数依赖注入。
你需要做的是继承FragmentFactory
并提供你的自定义实现,然后告诉fragment manager构建fragment时使用这个factory。
// 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)
...
}
}
还有就是Android社区习惯创建fragment的实例然后传递它到fragment transaction。
supportFragmentManager
.beginTransaction()
.replace(R.id.fragment_container, SecondFragment())
.commit()
在这个新版本中,FragmentTransaction
有一些新的重载允许你传递Class<Fragment>
,然后它会被传递到FragmentFactory
创建fragment的实例。因此上面例子的代码会变得像这样。
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, SecondFragment::class.java, args = null)
.commit()
使用FragmentScenario测试fragment
我们都有依赖Dagger获取依赖的fragment。现在使用FragmentFactory
可以很容易使用构造函数注入替换Dagger注入。这也让测试这些fragment变得容易,不用担心通过dagger注入mocks。
androidx.fragment:fragment-testing
也进行了更新允许你在测试时提供factory构建fragment。这意味着你可以在fragment中使用假的factory通过构造函数注入假依赖。这个是例子:
val scenario = launchFragmentInContainer<FirstFragment>(factory = MockFactory())
// Your test action
scenario.onFragment { fragment ->
// Your test assertions
}
总结
新发布的androidx.fragment
对于简化fragment的使用迈出了很大一步。至少对我来说易于配置和测试是很友好的改变,我也希望你找到对你有用的。
对于本版本中包含的所有更改请参考官方发版文档。
如果你想测试这些在build.gradle
添加依赖:
def fragment_version = "1.2.0-rc01"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
implementation "androidx.fragment:fragment-testing:$fragment_version"
网友评论