一、更多XML代码迁移
现在,我们可以更轻松地将界面缺少的内容补充完整:浇水信息和植物说明。按照前面介绍的XML代码方法,您已经可以迁移界面的其余部分了。
您之前从fragment_plant_detail.xml
移出的浇水信息XML代码由两个ID为plant_watering_header
和plant_watering
的TextView组成。
<TextView
android:id="@+od/plant_watering_header"
...
android:layout_marginStart="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_normal"
android:layout_marginEnd="@dimen/margin_small"
android:gravity="center_horizontal"
android:text="@string/watering_needs_prefix"
android:textColor="?attr/colorAccent"
android:textStyle="bold"
... />
<TextView
android:id="@+id/plant_watering"
...
android:layout_marginStart="@dimen/margin_small"
android:layout_marginEnd="@dimen/margin_small"
android:gravity="center_horizontal"
app:wateringText="@{viewModel.plant.wateringInterval}"
... />
与您之前的操作类似,请创建一个名为PlantWater
的新可组合项并添加Text
,以在界面上显示浇水信息:
PlantDetailDescription.kt
@Composable
private fun PlantWatering(wateringInterval: Int) {
Column(Modifier.fillMaxWidth()) {
// Same modifier used by both Texts
val centerWithPaddingModifier = Modifier
.padding(horizontal = dimensionResource(R.dimen.margin_small))
.align(Alignment.CenterHorizontally)
val normalPadding = dimensionResource(R.dimen.margin_normal)
Text(
text = stringResource(R.string.watering_needs_prefix),
color = MaterialTheme.color.primaryVariant,
fontWeight = FontWeight.Bold,
modifier = centerWithPaddingModifier.padding(top = normalPadding)
val wateringIntervalText = LocalContext.current.resources.getQuantityString(R.plurals.watering_needs_suffix, wateringInterval, wateringInterval)
)
Text(
text = wateringIntervalText,
modifier = centerWithPaddingModifier.padding(bottom = normalPadding)
)
}
}
@Preview
@Composable
private fun PlantWateringPreview() {
MaterialTheme {
PlantWatering(7)
}
}
预览如下:
我们将各个部分组合在一起,然后同样从PlantDetailContent
调用PlantWatering
。我们一开始移除的ConstraintLayout XML代码的外边距为16.dp
,我们需要将该值添加到Compose代码中。
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/margin_normal">
请在PlantDetailContent
中创建一个Column
以同时显示名称和浇水信息,并将其作为内边距。另外,为了确保北京颜色和所用的文本颜色均合适,请添加Surface
用于处理这种设置。
PlantDetailDescription.kt
@Composable
fun PlantDetailContent(plant: Plant) {
Surface {
COlumn(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
PlantName(plant.name)
PlantWatering(plant.wateringInterval)
}
}
}
刷新预览后,您会看到以下内容:
二、Compose代码中的View
现在,我们来迁移植物说明。fragment_plant_detail.xml
中的代码具有包含app:renderHtml="@{viewModel.plant.description}"
的TextView
,用于告知XML在界面上显示那些文本。renderHtml
是一个绑定适配器,可在PlantDetailBindingAdapter.kt
文件中找到。该实现使用HtmlCompat.fromHtml
在TextView
上设置文本!
但是,Compose目前不支持Spanned
类,也不支持显示HTML格式的文本。因此,我们需要在Compose代码中使用View系统中的TextView
来绕过此限制。
由于Compose目前还无法呈现HTML代码,因此你你需要使用AndroidView
API程序化地创建一个TextView
,从而实现此目的。
AndroidView
接受View
作为参数,并在View已膨胀时为您提供回调。
注意:AndroidView接受程序化地创建的View。如果您想嵌入XML文件,可以结合使用视图绑定与androidx.compose.ui:ui-viewbinding
库中的AndroidViewBinding
API。
为此,请创建新的PlantDescription
可组合项。次可组合项使用我们刚刚在lambda中保存的TextView
调用AndroidView
。在factory
回调中,请初始化使用给定Context
来回应HTML交互的TextView
。在update
回调中,用已保存的HTML格式的说明设置文本。
PlantDetailDescription.kt
@Composable
private fun PlantDescription(description: String) {
// Remembers the HTML formatted description. Re-executes on a new description
val htmlDescription = remember(description) {
HtmlCompat.fromHtml(description, HtmlCompat.FEOM_HTML_MODE_COMPACT)
}
// Displays the TextView on the screen and updates with the HTML description when inflated
// Updates to htmlDescription will make AndroidView recompose and update the text
AndroidView(
factory = { context ->
TextView(context).apply {
movementMethod = LinkMovementMethod.getInstance()
}
},
update = {
it.text = htmlDescription
}
)
}
@Preview
@Composable
private fun PlantDescriptionPreview() {
MaterialTheme {
PlantDescription("HTML<br><br>description")
}
}
预览:
请注意,htmlDescription
会记住作为参数传递的指定description
的HTML说明。如果description
参数发生变化,系统会再次执行remember
中的htmlDescription
代码。
同样,如果htmlDescription
发生变化,AndroidView
更新会掉会重组。在会掉中读取的任何状态都会导致重组。
我们将PlantDescription
添加到PlantDetailContent
可组合项,并更改预览代码,以便同样显示HTML说明:
PlantDetailDescription.kt
@Composable
fun PlantDetailContent(plant: Plant) {
Surface {
Column(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
PlantName(plant.name)
PlantWatering(plant.wateringInterval)
PlantDescription(plant.description)
}
}
}
@Preview
@Composable
private fun PlantDetailContentPreview() {
val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
MaterialTheme {}
}
预览如下:
现在,您已将原始ConstraintLayout
中的所有内容迁移到Compose。您可以运行该应用,检查其是否按预期运行。