迁移到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()
}

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

相关推荐
雨白1 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹3 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空5 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭5 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日6 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安6 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑6 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟10 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡12 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0012 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体