1、简介

1.1、学习内容

您将学习:

  • 你能够遵从的不同搬迁路径
  • 怎么逐渐将运用搬迁到Compose
  • 怎么将Compose增加到运用View构建的现有界面
  • 怎么在Compose中运用View
  • 怎么在Compose中运用根据View的主题
  • 怎么测验运用View和Compose编写的混合界面

1.2、前提条件

  • 有运用Kotlin语法(包括lambda)的经验
  • 了解Jetpack Compose(第二趴)——Compose 基础知识(上) 、Jetpack Compose(第二趴)——Compose 基础知识(下)。

所需条件

  • 最新版Android Studio

2、搬迁简介

JetPack Compose从设计之初就考虑到了View互操性能。如需搬迁到Compose,咱们建议您履行增量搬迁(Compose和View在代码库中共存),直到运用彻底搬迁至Compose停止。

推荐的搬迁战略如下:

  1. 运用Compose构建新功用
  2. 在构建功用时,确认可重复运用的元素,并开端创立常见界面组件库。
  3. 以此替换一个界面的现有功用。

2.1、运用Compose构建新功用

运用它Compose构建新功用是进步Compose选用率的最佳办法。这样,您增加的新功用就能够利用Compose的优势了。

一项新功用或许包括整个界面,在这种状况下,整个界面都是Compose中。假如您运用的是根据fragment的导航,这意味着您需求创立新的fragment,并在Compose中增加其内容。

另一方面,假如您构建的新功用是现有界面的一部分,则View和Compose将共存在同一个界面上。例如,假定您要增加的功用是RecyclerView中的一种新的视图类型。在这种状况下,新的视图类型将坐落Compose中,而其他项目保持不变。

2.2、构建常见界面组件库

运用Compose构建功用时,您很快就会意识到,您最终会构建组件库。您需求确认可重复运用的组件,促使在运用中重复运用这些组件,以便共享组件具有单一可信来历。您构建的功用随后能够依赖于这个库。

2.3、运用Compose替换现有功用

除了构建新功用之外,您还需求逐渐将运用中的现有功用搬迁到Compose。详细选用哪种办法由您决定,下面是一些合适的办法:

  1. 接单界面 – 包括少量界面元素和动态元素(例如欢迎界面、确认界面或设置界面)的简略界面。这些界面非常合适搬迁到Compose,因为只需几行代码就能搞定。
  2. 混合View和Compose界面 – 已包括少量Compose代码的界面是另一个不错的挑选,因为您能够继续逐渐搬迁该界面中的元素。假如您的某各界面在Compose中只需一个子树,您能够继续搬迁该树的其他部分,知道整个界面坐落Compose中。这称为自下而上的搬迁办法。

Jetpack Compose(第三趴)——迁移到Jetpack Compose

3、准备工作

原始展现UI:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

4、Sunflower中的Compose

咱们以一个例子来解说

首要我沃恩翻开运用级build.gradle文件后,查看该文件怎么导入Compose依赖项,以及怎么导入Compose依赖项,以及怎么运用buildFeatures { compose true }标志让Android Studio能够运转Compose。

app/build.gradle

android {
    // ...
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        // ...
        compose true
    }
    composeOptions {
        kotlinCompilerExtensionVersion '1.3.2'
    }
}
dependencies {
    // ...
    // Compose
    def composeBom = platform('androidx.compose:compose-bom:2022.10.00')
    implementation(composeBom)
    androidTestImplementation(composeBom)
    implementation "androidx.compose.runtime:runtime"
    implementation "androidx.compose.ui:ui"
    implementation "androidx.compose.foundation:foundation"
    implementation "androidx.compose.material:material"
    implementation "androidx.compose.runtime:runtime-livedata"
    implementation "androidx.compose.ui:ui-tooling"
    implementation "com.google.accompanist:accompanist-themeadapter-material:0.28.0"
    // ...
}

这些依赖项的版别在项目级build.gradle文件中界说。

5、欢迎运用Compose

在植物概况界面中,咱们需求将对植物的说明书搬迁到Compose,一起让界面的整体结构保持完好。

Compose需求有宿主activity或fragment才干出现界面。在Sunflower中,一切界面都运用fragment,因而你需求运用ComposeView:这是Android View能够运用其setContent办法办法保管setContent办法保管Compose界面内容。

5.1、移除XML代码

咱们先从搬迁开端!翻开fragment_plant_detail.xml并履行以下操作: 1. 切换到代码视图 2. 移除NestedScrollView中的ConstraintLayout代码和嵌套的4个TextView 3. 增加一个ComposeView,它会改为保管Compose代码,并以compose_view作为视图ID

fragment_plant_detail.xml

<androidx.core.widget.NextedScrollView
    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.comstraintLayout.widget.ConstraintLayout>
   <!-- End Step 2) Comment out until here -->
   <!-- Step 3) Add a ComposeView to host Compose code -->
   <androidx.compose.ui.platform.ComposeView
       android:id="@+id/compose_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</androidx.core.widget.NestedScrollView>

5.2、增加Compose代码

现在,您能够开端将植物概况页界面搬迁到Compose了!

在整个Codelab中,您都需求将Compose代码增加到plantdetail文件夹下的PlantDetailDescription.kt文件夹中。翻开该文件,看看项目中是否有占位符Hello Compose文本。

PlantDetailDescription.kt

fun PlantDetailDescription() {
    Surface {
        Text("Hello Compose")
    }
}

咱们从在上一步中增加ComposeView中调用此可组合项,即可在界面上显现此内容。翻开PlantDetailFragment.kt

界面运用的是数据绑定,因而您能够直接拜访composeView并调用setContent,以便在界面上显现Compose代码。您需求在MaterialTheme哪调用PlantDetailDescription可组合项,因为Sunflower运用的是Material Design。

PlantDetailFragment.kt

class PlantDetailFragment: Fragment() {
    // ...
    override fun onCreateView(...): View? {
        val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(inflate, R.layout.fragment_plant_detail, container, false
        ).apply {
            // ...
            composeView.setContent {
                // You're in Compose world!
                MaterialTheme {
                    PlantDetailDescription()
                }
            }
        }
        // ...
    }
}

Jetpack Compose(第三趴)——迁移到Jetpack Compose

6、运用XML创立可组合项

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

<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}"
    android:textAppearance="?attr/textAppearanceHeadline5"
    .../>

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

怎么观察LivData将稍后介绍,因而先假定咱们有可用的称号,并以参数形式将其传递到咱们在PlantDetailDescription.kt文件中创立的新PlantName可组合项。稍后,将从PlantDetailDescription可组合项调用此可组合项。

PlantDetailDescription.kt

@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")
    }
}

预览如下:

Jetpack Compose(第三趴)——迁移到Jetpack Compose
其中:

  • Text的款式为MaterialTheme.typography.h5,相似于XML代码中的textAppearanceHeadline5

  • 润饰符会润饰Text,使其看起来像XML版别:

  • 运用fillMaxWidth润饰符,使其占据最大可用宽度。此润饰符对应XML代码中layout_width特点的match_parent值。

  • 运用padding润饰符,以便运用水平内边距距值margin_small。这对应于XML中的marginStartmarginEnd声明。margin_small值也是运用dimensionResource辅佐函数提取的现有尺度资源。

  • wrapContentWidth润饰符用于对齐文本,以使其水平居中。这相似于在XML中gravitycenter_horizontal

    留意:Compose提供了从dimens.xml和strings.xml文件获取值的简略办法,即由此一来,您能够将View体系视为可信来历。

7、ViewModel和LiveData

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

7.1、ViewModel

因为在fragment中运用PlantDetailViewModel的实例,因而咱们能够将其作为参数传递给PlantDetailDescription,就这么简略。

PlantDetailDescription.kt

@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
    // ...
}

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

PlantDetailFragment.kt

class PlantDetailFragment: Fragment() {
    ...
    override fun onCreateView(...): View? {
        ...
        composeView.setContent {
            MaterialTheme {
                PlantDetailDescription(plantDetailViewModel)
            }
        }
    }
}

7.2、LiveData

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

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

留意:LiveData.observeAsState()开端观察LiveData,并以State目标表示它的值。每次向LiveData发布一个新值时,回来的State都会更新,这会导致一切State.value用例重组。

因为LiveData发出的值能够是null,因而您需求将其用例封装在null查看中。有鉴于此,以及为了完成可重用性,最好将LiveData的运用和监听拆分到不同的可组合项中。因而,咱们来创立一个名为PlantDetailContent的新可组合项,用于显现Plant信息。

完成这些更新后,PlantDetailDescription.kt文件现在应如下所示: PlantDetailDescription.kt

@Composable
fun PlantDetailDescription(plantDetailViewModel: 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

Jetpack Compose(第三趴)——迁移到Jetpack Compose

现在,您已连接ViewModel,使植物称号在Compose中显现。在接下来的几部分中,您将构建其余可组合项,并以相似的办法将它们连接到ViewModel。

8、更多XML代码搬迁

现在,咱们能够更轻松地将界面中缺少的内容补充完整:洒水信息和植物说明。您现已能够按照之前相似的办法搬迁界面的其余部分了。

您之前从fragment_plant_detail.xml移除的洒水信息XML代码由两个ID位plant_watering_headerplant_watering的TextView组成。

<TextView
    android:id="@+id/plant_watering_header"
    ...
    android:layout_marginStart="@dimen/margin_small"
    android:layout_marginTop="@dimen/margin_normal"
    android:layout_marginEnd="@dimen/margin_normal"
    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:watingText="@{viewModel.plant.wateringInterval}"
    .../>

与您之前的操作相似,请创立一个名为PlantWatering的新可组合项并增加Text可组合项,以在界面上显现洒水信息: PlantDetailDescription.kt

@OptIn(ExperimentalComposeUiApi::class)
@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.colors.primaryVariant,
            fontWeight = FontWeight.Bold,
            modifier = centerWithPaddingModifier.padding(top = normalPadding)
        )
        val wateringIntervalText = pluraStringResource(
            R.plurals.watering_needs_suffix, wateringInterval, wateringInterval
        )
        Text(
            text = wateringIntervalText,
            modifier = centerWithPaddingModifier.padding(bottom = normalPadding)
        )
    }
}
@Preview
@Composable
private fun PlantWateringPreview() {
    MaterialTheme {
        PlantWatering(7)
    }
}

预览图如下:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

需求留意以下几点:

  • 因为Text可组合项会共享水平内边距和对齐润饰,因而您能够将润饰符跟配给局部变量(即centerWithPaddingModifier),以重复运用润饰符。润饰符是标准的Kotlin目标,因而能够重复运用。
  • Compose的MaterialThemeplant_watering_header中运用的colorAccent不彻底匹配。现在,咱们能够运用将在互操作性主题设置部分中加以改进的MaterialTheme.colors.primaryVariant
  • 在Compose1.2.1中,有必要挑选启用ExperimentalComposeUiApi才干运用pluralStringResource。在将来的Compose版别中,或许不再需求这样做。

咱们将各个部分组合在一起,然后相同从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.waterInterval)
        }
    }
}

改写预览后,你会看到以下内容:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

9、Compose代码中的View

现在,咱们来搬迁植物说明。fragment_plant_detail.xml中的代码具有包括app:renderHtml="@{viewModel.plant.description}"TextView,用于奉告XML在界面上显现哪些文本。renderHtml是一个绑定适配器,可在PlantDetailBindingAdapter.kt文件中找到。该完成运用HtmlCompat.fromHtmlTextView上设置文本!

但是,Compose目前不支持Spanned类,也不支持显现HTML格局的文本。因而,咱们需求在Compose代码中运用View体系中的TextView来绕过此限制。

因为,Compose目前还无法出现HTML代码,因而你需求运用AndroidView API程序化地创立一个TextView,然后完成此目的。

AndroidView使您能够在Vire的factorylambda中构建该View。它还提供了一个updatelambda,它会在View膨胀和后续重组时被调用。

为此,请创立新的PlantDescription可组合项。可组合项将调用AndroidView,后者会在factorylambda中结构TextView。在factorylambda中,初始化显现HTML格局文本的TextView,然后将movementMethod设置为LinkMovementMethod的实例。最后,在updatelambda中的TextView的文呢么射中为htmlDescription

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.FROM_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")
    }
}

预览:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

请留意,htmlDescription会记住作为参数传递的指定description的HTML说明。假如description参数产生变化,体系会再次履行remember中的htmlDescription代码。

因而,假如htmlDescription产生变化,AndroidView更新会回调将重组。在updatelambda中读取的任何状况都会导致重组。

咱们将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 {
        PlantDetailContent(plant)
    }
}

预览如下:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

现在,您现已将原始ConstraintLayout中的一切内容搬迁到Compose。您能够运转运用,查看其是否按预期运转。

Jetpack Compose(第三趴)——迁移到Jetpack Compose

10、ViewCompositionStrategy

只需ComposeView与窗口别离,Compose就会处理组合。假如Fragment中运用了ComposeView,这种状况是不可取的,原因有两个:

  • 组合有必要遵从fragment的视图生命周期,Compose界面View类型才干保存状况。
  • 产生过渡时,底层ComposeView将处于别离状况。不过,在这些过渡期间,Compose界面元素仍然可见。

假如修改此行为,请运用适当的ViewCompositionStrategy调用setViewCompositionStrategy,使其改为遵从fragment的视图生命周期。详细而言,您需求在fragment的LifecycleOwner被销毁时运用DisponseOnViewTreeLifecycleDestroyed战略处置组合、

因为PlantDetailFragment包括进入和退出过渡,并且咱们稍后会在Compose中运用View类型,因而咱们需求保证ComposeView运用DisposeOnViewTreeLifecycleDestroyed战略。不过,在fragment中运用ComposeView时,最好一直设置此战略。

PlantDetailFragment.kt

import androidx.compose.ui.platform.ViewCompositionStrategy
...
class PlantDetailFragment: Fragment() {
    ...
    override fun onCreateView(...): View? {
        val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(inflate, R.layout.fragment_plant_detail, container, false
        ).apply {
            ...
            composeView.apply {
                // Dispose the Composition when the view's LifecycleOwner
                // is destroyed
                setViewCompositionStrategy(
                    ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
                )
                setContent {
                    MaterialTheme {
                        PlantDetailDescription(plantDetailViewModel)
                    }
                }
            }
        }
    }
}

11、互操作性主题设置

咱们已将朱武概况的文本内容搬迁到Compose。不过,您或许现已留意到,Compose运用的主题色彩有误。当植物称号应该运用绿色时,它运用的是紫色。

在这个搬迁的早期阶段,您或许需求Compose承继View体系中可用的主题,而不是从头开端在Compose中重新编写您自己的Material主题。Material主题可与Compose附带的一切Material Design组件完美配合运用。

如需在Compose中重复运用View体系的Material Design Components(MDC)主题,您能够运用Accompanist Material Theme Adapter库。MdcTheme函数将主动读取逐级上下文的MDC主题,并代表您将它们传递给MateriamTheme,以用于淡色和深色主题。即使您只需求适用于此Codelab的主题色彩,该库也会读取Vide体系的形状和排版。

该库已包括在app/build.gradle文件中,如下所示:

...
dependencies {
    ...
    implementation "com.google.accompanist:accompanist-themeadapter-material:$rootProject.accompanistVersion"
}

如需运用词库,请不要运用MaterialTheme,改为运用MdcTheme。例如,在PlantDetailFragment

PlantDetailFragment.kt

class PlantDetailFragment: Fragment() {
    ...
    composeView.apply {
        ...
        setContent {
            MdcTheme {
                PlantDetailDescription(plantDetailViewModel)
            }
        }
    }
}

此外还有PlantDetailDescription.kt文件中的一切预览可组合项:

PlantDetailDescription.kt

@Preview
@Composable
private fun PlantDetailContentPreview() {
    val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
    MdcTheme {
        PlantDetailContent(plant)
    }
}
@Preview
@Composable
private fun PlantNamePreview() {
    MdcTheme {
        PlantName("Apple")
    }
}
@Preview
@Composable
private fun PlantWateringPreview() {
    MdcTheme {
        PlantWatering(7)
    }
}
@Preview
@Composable
private fun PlantDescriptionPreview() {
    MdcTheem {
        PlantDescroption("HTML<br><br>description")
    }
}

在预览中您能够看到,MdcTheme会从style.xml文件中的主题中提取色彩。

Jetpack Compose(第三趴)——迁移到Jetpack Compose

您卡能够在深色主题中预览界面,办法是创立新函数并将Configuration.UI_MODE_NIGHT_YES传递给预览的uiMode

import android.content.res.Configuration
...
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PlantDetailContentDarkPreview() {
    val plant = Plant("id", "Apple", "HTML<br><br>description", 3, 30, "")
    MdcTheme {
        PlantDetailContent(plant)
    }
}

预览如下:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

假如您运转运用,它在淡色主题和深色主题下的行为都将与搬迁前彻底相同:

Jetpack Compose(第三趴)——迁移到Jetpack Compose

12、测验

将植物概况界面的各个部分搬迁到Compose之后,务必要进行测验,保证您没有损坏任何内容。

留意:在真实运用中,假如没有测验,则不应该重写旧代码。将代码搬迁到Compose时,您还应该重构测验并保证测验结果合格。

在Sunflower中,坐落androidTest文件夹的PlantDetailFragmentTest用于测验运用的某些功用。ging翻开该文件并查看当时的代码:

  • testPlantName用于查看界面上的植物称号。
  • testShareTextIntent用于查看点按共享按钮后是否触发了正确的intent

当activity或fragment运用Compose时,您不需求运用ActivityScenarioRule,而需求运用createAndroidComposeRule,它将ActivityScenarioRuleComposeTestRule集成,让您能够测验Compose代码。

PlantDetailFragmentTest中,将用法ActivityScenarioRule替换为createAndroidComposeRule。假如需求运用activity规矩来装备测验,请运用createAndroidComposeRule中的activityRule特点,详细代码如下所示:

@RunWith(AndroidJunit4::class)
class PlantDetailFragmentTest {
    @Rule
    @JvmField
    val composeTestRule = createAndroidComposeRule<GardenActivity>()
    ...
    @Before
    fun jumpToPlantDetailFragment() {
        populateDatabase()
        composeTestRule.activityRule.scenario.onActrivity { gardenActivity ->
        activity = gardenActivity
        val bundle = Bundle().apply {
            putString("plantId", "malus-pumila")
        }
        findNavController(activity, R.id.nav_host).navigate(R.id.plant_detail_fragment, bundle)
        }
    }
}

假如您运转测验,testPlantName会失利!testPlantName查看界面上是否存在TextView。不过,您已将这部分的界面搬迁Compose。因而,您需求改用Compose断语:

@Test
fun testPlantName() {
    composeTestRule.onNodeWithText("Apple").assertIsDisplayed()
}

假如运转测验,您会看到一切测验均会经过。

Jetpack Compose(第三趴)——迁移到Jetpack Compose

13、恭喜

恭喜,您已成功完成此 Codelab!

原始 Sunflower GitHub 项目的compose分支会将植物详细信息界面彻底搬迁到 Compose。除了您在此 Codelab 中完成的操作之外,该分支还会模拟 CollapsingToolbarLayout 的行为。这些行为包括:

  • 运用 Compose 加载图片
  • 动画
  • 更超卓的尺度处理
  • 等等!

翻译原文:Compose 基础知识