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

相关推荐
帅得不敢出门10 小时前
安卓设备adb执行AT指令控制电话卡
android·adb·sim卡·at指令·电话卡
我又来搬代码了11 小时前
【Android】使用productFlavors构建多个变体
android
德育处主任13 小时前
Mac和安卓手机互传文件(ADB)
android·macos
芦半山13 小时前
Android“引用们”的底层原理
android·java
迃-幵14 小时前
力扣:225 用队列实现栈
android·javascript·leetcode
大风起兮云飞扬丶14 小时前
Android——从相机/相册获取图片
android
Rverdoser14 小时前
Android Studio 多工程公用module引用
android·ide·android studio
aaajj14 小时前
[Android]从FLAG_SECURE禁止截屏看surface
android
@OuYang14 小时前
android10 蓝牙(二)配对源码解析
android
Liknana14 小时前
Android 网易游戏面经
android·面试