迁移到Jetpack Compose 第四趴

一、ViewCompositionStrategy

默认情况下,只要ComposeView与窗口分离,Compose就会处理组合。如果fragment中使用ComposeView,默认设置是不可取的,这有多种原因:

  • 组合必须遵循fragment的视图晟敏周期,Compose界面View类型才能保存状态,并且在发生过渡或窗口过渡时让Compose界面元素保留在界面上。在过渡期间,ComposeView本身仍然可见,即使其与窗口分离也是如此。

您可以手动调用AbstractComposeView.disposeComposition方法来手动处理组合。或者,如需在不再需要组合时自动处理组合,请通过调用setViewCompositionStrategy方法设置其他策略或创建自己的策略。

在fragment的LifecycleOwner被销毁时,使用DisposeOnViewTreeLifecycleDestroyed策略处理组合。

由于PlantDetailFragment包含进入和退出过渡,并且哦我们稍后会在Compose中使用View类型,因此我们需要确保ComposeView使用DisposeOnViewTreeLifecycleDestroyed策略。不过,只要是在fragment中使用ComposeView,最好都设置此策略。

plantdetail/PlantDetailFragment.kt

kotlin 复制代码
import androidx.compose.ui.platform.ViewCompositionStrategy
...

class PlantDetailFragment: Fragment() {
    ...
    override fun onCreateView(...): View? {
        val binding = DetailBindingUtil.inflate<FragmentPlantDetailBinding>(inflate, R.layoutt.fragment_plant_detail, container, false).apply {
            ...
            composeView.apply {
                // Dispose the Composition when the view's LifecycleOwner
                // is destroyer
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyer
                )
                setContent {
                    MaterialTheme {
                        PlantDetailDescription(plantDetailViewModel)
                    }
                }
            }
        }
    }
}

二、互操作性主题设置

我们已将植物详情的文本内容迁移到Compose。不过,您可能已经注意到,Compose使用的主题颜色有误。当植物名称应使用绿色时,它使用的是紫色。

这个迁移的早期阶段,您可能需要Compose继承View系统中可用的主题,而不是从头开始在Compose中重新编写您的Material主题。Material主题课与Compose附带的所有Material Design组件完美配合使用。

如需在Compose中重复使用View系统的Material Design组件(MDC)主题,您可以使用compose-theme-adapterMdcTheme函数将自动读取主机上下文的MDC主题,并代表您将它们传递给MaterialTheme,以用于浅色和深色主题。即使您只需要使用此主题颜色,该库也会读取View系统的形状和排版。

该库已包含在app/build.gradle文件中,如下所示:

bash 复制代码
...
dependencies {
    ...
    implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
    ...
}

如需使用此库,请将MaterialTheme的用法替换为MdcTheme。例如,在PlantDetailFragment中:

PlantDetailFragment.kt

kotlin 复制代码
class PlantDetailFragment: Fragment() {
    ...
    compose.apply {
        ...
        setContent {
            MdcTheme {
                PlantDetailDescription(plantDetailViewModel)
            }
        }
    }
}

此外还有PlantDetailDescription.kt文件中的所有预览可组合项:

PlantDetailDescription.kt

kotlin 复制代码
@Preview
@Composable
private fun PlantDetailCotnentPreview() {
    val palnt = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
    MdcTheme {
        PlantDetailContent(plant)
    }
}

@Preview
@Composable
private fun PlantNamePreview() {
    MdcTheme {
        PlantName("Apple")
    }
}

@Preview
@Compoable
privat fun PlantWateringPreview() {
    MdcTheme {
        PlantWatering(7)
    }
}

@Preview
@Composable
private fun PlantDescriptionPreview() {
    MdcTheme {
        PlantDescription("HTML<br><br>description")
    }
}

在预览中您可以看到,MdcTheme会从styles.xml文件中的主题中提取颜色。

您还可以在深色主题中预览界面,方法是创建新函数并将Configuration.UI_MODE_NIGHT_YES传递给预览的uiMode:

kotlin 复制代码
import android.content.res.Configuration
...

@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlantDetailContentDarkPreview() {
    cal plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
    MdcTheme {
        PlantDetailContent(plant)
    }
}

三、测试

将植物详情界面的部分内容迁移到Compose之后,务必要进行测试,以确保您没有损坏任何内容。

在Sunflower中,位于androidText文件夹的PlantDetailFragmentTest用于测试应用的某些功能。请打开文件并检查当前的代码:

  • testPlantName用于检查界面上的植物名称
  • testShareTextIntent用于检查点按分享按钮后是否触发了正确的intent

当activity或fragment使用Compose时,您不需要使用ActivityScenarioRule,而需要使用createAndroidComposeRule,它将ActivityScenarioRuleComposeTestRule集成,让您可以测试Compose代码。

PlantDetailFragmentTest中,将用法ActivityScenarioRule替换为createAndroidComposeRule。如果需要使用activity规则来配置测试,请使用createAndroidComposeRule中的activityRule属性,具体代码如下所示:

less 复制代码
@RunWith(AndroidJUnit4::class)
class PlantDetaimFragmentTest {
    @Rule
    @JvmField
    val composeTestRule = createAndroidComposeRule<GardenActivity>()
    
    ...
    
    @Before
    fun jumpToPlantDetailFragment() {
        populateDatabase()
        
        composeTestRule.activityRule.scenario.onActivity { gardenActivity ->
            activity = gardenActivity
            
            val bundle = Bundle().apply { putString("plantId", "malus-pumila") }
            findNavContriller(activity, R.id.nav_host).navigate(R.id.plant_detail_fragment, bundle)
        }
    }
}

如果您运行测试,testPlantName会失败!testPlantName检查界面上是否存在TextView。不过,您已将这部分的界面迁移到Compose。因此,您需要改用Compose断言:

kotlin 复制代码
@Test
fun testPlantName() {
    composeTestRule.onNodeWithText("Apple").assertIsDisplayer()
}

如果运行测试,您会看到所有测试均会通过。

相关推荐
谢白羽8 小时前
vllm实践
android·vllm
电子云与长程纠缠8 小时前
Godot学习03 - 实例化、层级访问、Export
android·学习·godot
毕设源码-朱学姐9 小时前
【开题答辩全过程】以 基于Android的便民系统的设计与实现为例,包含答辩的问题和答案
android
鬼蛟9 小时前
Spring————事务
android·java·spring
qq_1702647510 小时前
unity出安卓年龄分级的arr包问题
android·unity·游戏引擎
kejiashao12 小时前
Android View的绘制流程及事件分发机制
android
小蜜蜂嗡嗡12 小时前
flutter实现付费解锁内容的遮挡
android·flutter
进击的cc13 小时前
拒绝背诵!一文带你打穿 Android ANR 发生的底层全链路
android·面试
进击的cc13 小时前
App 启动优化全家桶:别再只盯着 Application 了,热启动优化你真的做对了吗?
android·面试
彭波39613 小时前
安卓手机端安装xapk、apkm软件!怎样安装xapk软件?安卓的apk和XAPK的区别?附教程
android·智能手机