迁移到Jetpack Compose 第二趴

一、Hello Compose!

在职务详情界面中,我们需要将对植物的说明迁移到Compose,同时让界面的总体结构保持完好。这时,您需要遵循"规划迁移"部分中提到的"搭配使用Compose和View"迁移策略。

Compose需要有宿主activity或fragment才能呈现界面。在Sunflower中,所有界面都使用fragment,因此您需要使用ComposeView:这一AndroidView可以使用其setContent方法托管Compose界面内容。

1.1、移除XML代码

我们先从迁移开始!打开fragment_plant_detail.xml并执行以下操作:

  1. 切换到代码视图
  2. 移除NestedScrollView中的ConstraintLayout代码和嵌套的TextView
  3. 添加一个ComposeView,它会改为托管Compose代码,并以compose_view作为视图ID

fragment_plant_detail.xml

ini 复制代码
<androidx.core.widget.NestedScrollView
    android:id="@+id/plant_detail_scrollview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    android:paddingBottom="@dimen/fab_bottom_padding"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
        
        // Step 2) Comment out ConstraintLayout and its children
        <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="@dimen/margin_normal">
        
        <TextView
            android:id="@+id/plant_detail_name"
        ...
        
    </androidx.constraintlayout.widget.ConstraintLayout>
    // End Step 2) Comment out until here
    
    // Step 3) Add a ComposeView to host Compose code
    <androidx.compose.ui.plantform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
        
</androidx.core.widget.NestedScrollView>
            

1.2、添加Compose代码

现在,您可以开始将植物详情界面迁移到Compose了!

整个操作中,您都需要将Compose代码添加到plantdetail文件夹下的PlantDetailDescription.kt文件中。打开该文件,看看项目中是否有占位符"Hello Compose!"文本。

plantdetail/PlantDetailDescription.kt

kotlin 复制代码
@Composable
fun PlantDetailDescription() {
    Text("Hello Compose")
}

我们从在上一步中添加的ComposeView中调用此可组合项,即可在界面上显示此内容。打开plantdetail/PlantDetailFragment.kt

界面使用的是数据绑定,因此您可以直接访问composeView并调用setContent,以便在界面上显示Compose代码。您需要在MaterialTheme内调用PlantDetailDescription可组合项,因为Sunflower使用的是Material Design。

plantdetail/PlantDetailFragment.kt

kotlin 复制代码
class PlantDetailFragment: Fragment() {
    ...
    override fun onCreateView(...): View? {
        val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(inflater, R.layout.fragmebt_plant_detail, container, false).apply {
            ...
            composeView.setContent {
                // You're in Compose world!
                MaterialTheme {
                    PlantDetailDecription()
                }
            }
        }
        ...
    }
}

如果您运行该应用,界面上会显示"Hello Compose!"

二、使用XML创建可组合项

我们首先迁移植物的名称。更确切地说,就是您在fragment_plant_detail.xml中移除的ID为@+id/plant_detail_nameTextView。XML代码如下:

ini 复制代码
<TextView
    android:id="@+id/plant_detail_name"
    ...
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginEnd="@dimen/margin_small"
    android:gravity="center_horizontal"
    android:text="@{viewModel.plant.name}"
    androiid:textAppearance="?attr/textAppearanceHeadline5"
    ... />

请查看它是否为textAppearanceHeadline5样式,水平外边距为8.dp,以及是否在界面上水平居中。不过,要显示的标题是由代码库层的PlantDetailViewModel公开的LiveData中观察到的。

如何观察LiveData将在稍后介绍,因此先假设我们有可用的名称,并以参数形式将其传递到我们在PlantDetailDescription.kt文件中创建的新PlantName可组合项。稍后,将从PlantDetailDescription可组合项调用此可组合项。

PlantDetailDescription.kt

kotlin 复制代码
@Composable
private fun PlantName(name: String) {
    Text(
        text = name,
        style = MaterialTheme.typography.h5,
        modifier = Modifier
            .fillMaxWidth()
            .padding(horizontal = dimensionResource(R.dimen.margin_small))
            .wrapContentWidth(Alignment.CenterHorizontally)
    )
}

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

其中:

  • Text的样式为MaterialTheme.typography.h5,它从XML代码映射到textAppearanceHeadline5

  • 修饰符会修饰Text,以将其调整为类似于XML版本:

  • fillMaxWidth修饰符对应于XML代码中的android:layout_width="match_parent"

  • margin_small的水平padding,其值是使用。dimensionResource辅助函数从View系统获取的。

  • wrapContentWidth水平对其Text

    注意:Compose提供了从dimens.xmlstrings.xml文件获取值的简单方法,即dimensionResource(id)stringResource(id)

    由此一来,您可以将View系统视为可信来源。

三、ViewModel和LiveData

现在,我们将标题链接到界面。如需执行此操作,您需要使用PlantDetailViewModel加载数据。为此,Compose集成了ViewModelLiveData

3.1、ViewModel

由于fragment中使用了PlantDetailViewModel的实例,因此我们可以将其作为参数传递给PlantDetailDescription,就这么简单。

注意:如果您遇到了ViewModel无法使用的情况,或者您不许忘将该依赖项传递给可组合项,则可以在可组合项中使用viewmodel函数,以获取ViewModel的实例。

可组合项没有自己的ViewModel实例,相应的实例将在可组合项和托管Compose代码的生命周期所有者(activity或fragment)之间共享。

打开PlantDetailDescription.kt文件,然后将PlantDetailViewModel参数添加到PlantDetailDescription:

PlantDetailDescription.kt

kotlin 复制代码
@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
    ...
}

现在,请在从fragment调用此可组合项时传递ViewModel实例:

PlantDetailFragment.kt

kotlin 复制代码
class PlantDetailFragment: Fragment() {
    ...
    override fun onCreateView(...): View? {
        ...
        composeView.setContent {
            MaterialTheme {
                PlantDetailDescription(plantDetailViewModel)
            }
        }
    }
}

3.2、LiveData

有了LiveData,您已有权访问PlantDetailViewModelLiveData<Plant>字段,以获取植物的名称。

如需从可组合项观察LiveData,请使用LiveData.observeAsState()函数。

注意: LiveData.observeAsState()开始观察LiveData,并通过State对象表示它的值。每次向LiveData发布一个新值时,返回的State都会更新,这会导致所有State.value用法重组。

由于LiveData发出的值可以为null,因此您需要将其用法封装在null检查中。有鉴于此,以及为了实现可重用性,最好将LiveData的使用和监听拆分到不同的可组合项中。因此,请创建一个名为PlantDetailContent的新可组合项,用于显示Plant信息。

基于以上原因,添加LiveData观察后,PlantDetailDescription.kt文件将如下所示。

PlantDetailDescription.kt

kotlin 复制代码
@Composable
fun PlantDetailDescription(plantDetaiViewModel: PlantDetailViewModel) {
    // Observes values coming from the VM's LiveData<Plant> field
    val plant by PlantDetailViewModel.plant.observeAsState()
    
    // If plant is not null, display the content
    plant?.let {
        PlantDetailContent(it)
    }
}

@Composable
fun PlantDetailContent(plant: Plant) {
    PlantName(plant.name)
}

@Preview
@Composable
private fun PlantDetailCOntentPreview() {
    val plant = Plant("id", "Apple", "description", 3, 30, "")
    MaterialTheme {
        PlantDetailContent(plant)
    }
}

预览与PlantNamePreview相同,因为PlantDetailContent目前只调用PlantName:


现在,您已完成在Compose中显示植物名称所需的所有ViewModel链接。在接下来的几部分中,您将构建其余可组合项,并以类似的方式将它们里链接到ViewModel。

相关推荐
千里马学框架20 小时前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台20 小时前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone20 小时前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc21 小时前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui