迁移到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。

相关推荐
非凡ghost6 分钟前
LSPatch官方版:无Root Xposed框架,自由定制手机体验
android·智能手机·软件需求
_extraordinary_6 分钟前
MySQL 库的操作 -- 增删改查,备份和恢复,系统编码
android·mysql·oracle
西瓜本瓜@3 小时前
在Android中如何使用Protobuf上传协议
android·java·开发语言·git·学习·android-studio
似霰6 小时前
安卓adb shell串口基础指令
android·adb
fatiaozhang95278 小时前
中兴云电脑W102D_晶晨S905X2_2+16G_mt7661无线_安卓9.0_线刷固件包
android·adb·电视盒子·魔百盒刷机·魔百盒固件
CYRUS_STUDIO9 小时前
Android APP 热修复原理
android·app·hotfix
鸿蒙布道师9 小时前
鸿蒙NEXT开发通知工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
鸿蒙布道师9 小时前
鸿蒙NEXT开发网络相关工具类(ArkTs)
android·ios·华为·harmonyos·arkts·鸿蒙系统·huawei
大耳猫10 小时前
【解决】Android Gradle Sync 报错 Could not read workspace metadata
android·gradle·android studio
ta叫我小白10 小时前
实现 Android 图片信息获取和 EXIF 坐标解析
android·exif·经纬度