方法
最近,我们有机会为我们正在开发的SDK重新设计示例应用程序。我们只是重新设计一个屏幕(主页),因此我们决定在应用程序中介绍Jetpack Compose
。
基于我们阅读的内容,大多数迁移或查看Interop帖子都是关于将Fragments
和fragment navigation
保持原样,并将片段的内容移动到ComposeView
中。实际上,official docs also mention that strategy.
为此,我们仍然必须触摸应用程序中的其他片段,而不仅仅是主页。因此,感觉比我们打算做的更多。最重要的是,示例应用程序广泛使用PreferenceFragmentCompat
用于各种设置屏幕。要将其内容移至ComposeView
,我们必须从头开始重新创建一种首选项屏幕,因为撰写没有直接替换PreferenceFragment
。
因此,我们决定采用Fragments in Compose方法。除此之外,我们还使用compose navigation component移动了导航。
碎片组成
布局
我们在每个片段中都为每个片段创建了新的布局文件。
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view_events"
android:name="com.example.fragment.analytics.AnalyticsEventsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.fragment.app.FragmentContainerView>
合成
我们编写了与AndroidViewBinding组合的通用FragmentHolder
。它创建了Android布局资源。我们上面创建的布局文件将生成他们的Binding
类,我们可以在AndroidViewBinding
中膨胀和传递为BindingFactory
@Composable
fun <T : ViewBinding> FragmentHolderScreen(
topBarTitle: String,
androidViewBindingFactory: (inflater: LayoutInflater, parent: ViewGroup, attachToParent: Boolean) -> T,
onBackPress: () -> Unit = {},
androidViewBindingUpdate: T.() -> Unit = {},
) {
Scaffold(
topBar = {
TopBar(title = topBarTitle, onBackPress = onBackPress)
},
content = { paddingValues ->
AndroidViewBinding(
factory = androidViewBindingFactory,
modifier = Modifier.padding(paddingValues),
update = androidViewBindingUpdate,
)
},
)
}
导航
创建组合后,我们用Screen
枚举定义了导航组件。
具有上述布局绑定和FragmentHolderScreen
的AnalyticsEventsFragment
,最小化的Scaffold
合奏看起来像这样,
Scaffold(
content = { contentPadding ->
NavHost(
navController = navController,
startDestination = Screen.Settings.route,
Modifier.padding(contentPadding),
) {
composable(Screen.Events.route) {
FragmentHolderScreen(
topBarTitle = getString(R.string.toolbar_title_events),
androidViewBindingFactory = ComposeFragmentEventsBinding::inflate,
androidViewBindingUpdate = {
with(fragmentContainerViewEvents.getFragment<AnalyticsEventsFragment>()) {
// Reference of AnalyticsEventsFragment is available here
}
},
onBackPress = {
onBackPress(navController)
},
)
}
}
},
)
内/孩子碎片
我们内部的一些片段有导航,用户将进入儿童片段。在进行基于作曲的导航时,我们添加了这些孩子碎片作为Screen/route
枚举的一部分。然后,我们在base
片段类中设置了一个lambda函数(val navigateTo: (Screen) -> Unit
),以处理单击操作以移至子片段。
共享流程
示例应用程序使用SharedPreferences
,我们没有时间转到DataStore
之类的东西。将一个屏幕移至Compose
之后,它仍然效果很好。我们只是确保在主要活动的onCreate
中定义SharedPreferences
的引用,然后将参考转移到可合曲。由于我们没有触摸现有的片段,因此他们使用的SharedPreferences
已经定义。
UI测试
我们在测试SDK的示例应用程序中进行了广泛的UI测试。将示例应用程序的主屏幕移至Compose
之后,我们打破了所有测试的入口点。
幸运的是,组成UI测试和浓缩咖啡测试彼此效果很好。您可以在单行测试中编写撰写测试,该测试是基于撰写的屏幕,第二行可以是观看视图层的意式浓缩板测试步骤。
我们围绕更新单元测试而面临的最大问题是两个系统之间的延迟。单击基于撰写的屏幕上的按钮后,该应用将转到SDK(基于视图的UI)。许多测试失败了,因为测试执行时,该视图将在屏幕上不可用。在尝试了几件事之后,除了在我们处理基于撰写的屏幕上单击按钮之前和之后添加Thread.sleep()
之外,没有什么好工作了。延迟可能给了浓缩咖啡足够的时间找到正确的基于视图的屏幕。
结论
总的来说,我们对整个实验的进行方式感到满意。我们学到了一些有关撰写视图Interop的信息,并有机会通过此博客文章分享我们的发现。
感谢您的阅读!如果您有疑问,请在评论中告诉我。另外,您可以在Twitter,LinkedIn或Kotlin Slack的@shaktiman_droid与我联系。而且,如果您发现所有这些有趣的事