1. Introdução
O Compose e o sistema de visualização podem trabalhar lado a lado.
Neste codelab, você vai migrar partes da tela de detalhes da planta do app Sunflower (link em inglês) para o Compose. Criamos uma cópia do projeto para você testar a migração de um app realista para o Compose.
Ao final do codelab, você vai poder continuar a migração e converter o restante das telas do Sunflower, se quiser.
Para receber mais suporte durante este codelab, confira as orientações neste vídeo (em inglês):
O que você vai aprender
Neste codelab, você vai aprender o seguinte:
- Os diferentes caminhos de migração que você pode seguir.
- Como migrar um app para o Compose gradualmente.
- Como adicionar o Compose a uma tela já criada com visualizações do Android.
- Como usar uma visualização do Android no Compose.
- Como usar o tema do sistema de visualização no Compose.
- Como testar uma tela com o código do sistema de visualização e o Compose.
Prerequisites
- Experiência com a sintaxe do Kotlin, incluindo lambdas.
- Conhecimento das noções básicas do Compose.
O que é necessário
2. Planejar a migração
A migração para o Compose depende de você e da sua equipe. Há muitas maneiras diferentes de integrar o Jetpack Compose a um app Android existente. Veja duas estratégias comuns de migração:
- Desenvolver uma nova tela com o Compose
- Usar uma tela existente e migrar os componentes dela gradualmente
Compose em novas telas
Uma abordagem comum ao refatorar o app para usar uma nova tecnologia é a adoção dela em novos recursos criados para o app. Neste caso, estamos falando de novas telas. Se você precisar criar uma nova tela da IU no app, use o Compose mesmo que o restante continue a usar o sistema de visualização.
Para isso, faça a interoperabilidade do Compose nas bordas dos recursos migrados.
Compose e visualizações juntos
Você pode migrar algumas partes de uma tela para o Compose e outras para o sistema de visualização. Por exemplo, você pode migrar uma RecyclerView, mas deixar o restante da tela no sistema de visualização.
Ou vice-versa: use o Compose como o layout externo e use algumas visualizações existentes que podem não estar disponíveis no Compose, como MapView ou AdView.
Concluir a migração
Migre fragmentos ou telas inteiras para o Compose, um de cada vez. Mais simples, mas muito generalizado.
E neste codelab?
Neste codelab, você vai fazer uma migração gradual para o Compose na tela de detalhes da planta no app Sunflower, trabalhando com o Compose e as visualizações ao mesmo tempo. Depois disso, você poderá continuar a migração por conta própria, se quiser.
3. Etapas da configuração
Buscar o código
Consiga o código do codelab no GitHub:
$ git clone https://github.com/googlecodelabs/android-compose-codelabs
Se preferir, faça o download do repositório como um arquivo ZIP:
Abrir o Android Studio
Este codelab exige o Android Studio Bumblebee.
Como executar o app de exemplo
Você fez o download de um código que contém todos os codelabs disponíveis do Compose. Para concluir este codelab, abra o projeto MigrationCodelab
no Android Studio.
Neste codelab, você vai migrar a tela de detalhes da planta do app Sunflower para o Compose. Você pode abrir a tela de detalhes tocando em uma das plantas disponíveis na lista mostrada no app.
Configuração do projeto
O projeto está disponível em várias ramificações do GitHub:
main
é a ramificação consultada ou transferida por download. Este é o ponto de partida do codelab.end
contém a solução deste codelab.
Recomendamos que você comece com o código na ramificação main
e siga o codelab passo a passo no seu ritmo.
Durante o codelab, você verá snippets de código que precisam ser adicionados ao projeto. Em alguns locais, também será necessário remover o código que é explicitamente mencionado nos comentários dos snippets de código.
Para acessar a ramificação end
usando o git, use o seguinte comando:
$ git clone -b end https://github.com/googlecodelabs/android-compose-codelabs
Ou faça o download do código da solução aqui:
Perguntas frequentes
4. Compose no app Sunflower
O Compose já foi adicionado ao código que você transferiu por download da ramificação main
. No entanto, vamos dar uma olhada no que é necessário para que ele funcione.
Se você abrir o arquivo app/build.gradle
(ou build.gradle (Module: compose-migration.app)
), vai ver como ele importa as dependências do Compose e permite que o Android Studio funcione com o Compose usando a sinalização buildFeatures { compose true }
.
app/build.gradle
android {
...
kotlinOptions {
jvmTarget = '1.8'
useIR = true
}
buildFeatures {
...
compose true
}
composeOptions {
kotlinCompilerExtensionVersion rootProject.composeVersion
}
}
dependencies {
...
// Compose
implementation "androidx.compose.runtime:runtime:$rootProject.composeVersion"
implementation "androidx.compose.ui:ui:$rootProject.composeVersion"
implementation "androidx.compose.foundation:foundation:$rootProject.composeVersion"
implementation "androidx.compose.foundation:foundation-layout:$rootProject.composeVersion"
implementation "androidx.compose.material:material:$rootProject.composeVersion"
implementation "androidx.compose.runtime:runtime-livedata:$rootProject.composeVersion"
implementation "androidx.compose.ui:ui-tooling:$rootProject.composeVersion"
implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
...
}
A versão dessas dependências é definida no arquivo build.gradle
raiz.
5. Olá, Compose!
Na tela de detalhes da planta, migraremos a descrição para o Compose, deixando a estrutura geral intacta. Você acompanhará a estratégia de migração do Compose e das visualizações juntos mencionada na seção Planejar a migração.
O Compose precisa de uma atividade ou fragmento do host para renderizar a IU. No Sunflower, como todas as telas usam fragmentos, você vai usar a ComposeView
: uma visualização do Android que pode hospedar conteúdo da IU do Compose com o método setContent
.
Como remover o código XML
Vamos começar a migração. Abra fragment_plant_detail.xml
e faça o seguinte:
- Mude para a Visualização "Code".
- Remova o código
ConstraintLayout
e asTextView
s aninhadas naNestedScrollView
. O codelab compara e referencia o código XML ao migrar itens individuais. É útil adicionar comentários ao código. - Adicione uma
ComposeView
para hospedar o código do Compose em vez dacompose_view
como o ID da visualização.
fragment_plant_detail.xml
<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.platform.ComposeView
android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.core.widget.NestedScrollView>
Como adicionar código do Compose
Agora, você já pode migrar a tela de detalhes da planta para o Compose.
Ao longo do codelab, você vai adicionar um código do Compose ao arquivo PlantDetailDescription.kt
na pasta plantdetail
. Abra o arquivo e veja como já existe um texto "Hello Compose!"
marcador de posição no projeto.
plantdetail/PlantDetailDescription.kt
@Composable
fun PlantDetailDescription() {
Text("Hello Compose")
}
Vamos exibir esse texto na tela chamando o elemento que pode ser composto do ComposeView
adicionado na etapa anterior. Abra plantdetail/PlantDetailFragment.kt
.
Como a tela está usando a vinculação de dados, você pode acessar diretamente a composeView
e chamar setContent
para exibir o código do Compose na tela. Chame o elemento PlantDetailDescription
que pode ser composto dentro do MaterialTheme
, já que o app Sunflower usa o Material Design.
plantdetail/PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
inflater, R.layout.fragment_plant_detail, container, false
).apply {
...
composeView.setContent {
// You're in Compose world!
MaterialTheme {
PlantDetailDescription()
}
}
}
...
}
}
Se você executar o app, a mensagem "Hello Compose!
" vai ser exibida na tela.
6. Como criar um elemento que pode ser composto fora do XML
Vamos começar migrando o nome da planta. Mais exatamente, a TextView
com o ID @+id/plant_detail_name
removido em fragment_plant_detail.xml
. Veja o código 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"
... />
Veja como a TextView tem um estilo textAppearanceHeadline5
, uma margem horizontal de 8.dp
e é centralizada horizontalmente na tela. No entanto, o título exibido é observado em um LiveData
exposto pelo PlantDetailViewModel
da camada do repositório.
Como a observação de um LiveData
vai ser abordada mais tarde, vamos supor que o nome esteja disponível e seja transmitido como um parâmetro para um novo PlantName
que pode ser composto no arquivo PlantDetailDescription.kt
. Esse elemento vai ser chamado mais tarde no 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")
}
}
Veja uma prévia
Em que:
- o estilo de
Text
éMaterialTheme.typography.h5
, que mapeia para otextAppearanceHeadline5
usando o código XML; - os modificadores decoram o elemento "Text" para o ajustar como a versão XML:
- o modificador
fillMaxWidth
corresponde aoandroid:layout_width="match_parent"
no código XML; - o
padding
horizontal demargin_small
, que é um valor do sistema de visualização, usa a função auxiliardimensionResource
; wrapContentWidth
é usado para alinhar oText
horizontalmente.
7. ViewModels e LiveData
Agora, vamos conectar o título à tela. Para fazer isso, carregue os dados usando o PlantDetailViewModel
. O Compose vem com integrações para ViewModel e LiveData.
ViewModels
Como uma instância do PlantDetailViewModel
é usada no fragmento, ela pode ser transmitida como um parâmetro para PlantDetailDescription
.
Abra o arquivo PlantDetailDescription.kt
e adicione o parâmetro PlantDetailViewModel
a PlantDetailDescription
:
PlantDetailDescription.kt
@Composable
fun PlantDetailDescription(plantDetailViewModel: PlantDetailViewModel) {
...
}
Agora, transmita a instância do ViewModel ao chamar esse elemento que pode ser composto no fragmento:
PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
...
composeView.setContent {
MaterialTheme {
PlantDetailDescription(plantDetailViewModel)
}
}
}
}
LiveData
Com isso, você já tem acesso ao campo LiveData<Plant>
do PlantDetailViewModel
para ver o nome da planta.
Para observar o LiveData em um elemento que pode ser composto, use a função LiveData.observeAsState()
.
Como os valores emitidos pelo LiveData podem ser nulos, você vai precisar agrupar o uso em uma verificação de valores nulos. Por causa disso, e para reutilização, é um bom padrão dividir o consumo de LiveData e o detectar em diferentes elementos que podem ser compostos. Assim, crie um novo elemento que pode ser composto com o nome PlantDetailContent
para exibir informações de Plant
.
Esta vai ser a aparência do arquivo PlantDetailDescription.kt
após a adição da observação do LiveData:
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)
}
}
Com a mesma visualização de PlantNamePreview
, já que, no momento, PlantDetailContent
chama PlantName
:
Você conectou o ViewModel por completo para exibir um nome de planta no Compose. Nas próximas seções, você vai criar o restante dos elementos que podem ser compostos e os conectar ao ViewModel de maneira semelhante.
8. Mais migração de código XML
Ficou mais fácil preencher o que falta na nossa IU: as informações de irrigação e a descrição da planta. Seguindo a mesma abordagem de antes, você já pode migrar o restante da tela.
O código XML das informações de irrigação removido de fragment_plant_detail.xml
consiste em duas TextViews com IDs plant_watering_header
e plant_watering
.
<TextView
android:id="@+id/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}"
.../>
Como você fez antes, crie um novo elemento que pode ser composto com o nome PlantWatering
e adicione Text
s para exibir as informações de irrigação na tela:
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.colors.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)
}
}
Veja uma prévia
Algumas coisas a observar:
- Como o padding horizontal e a decoração do alinhamento são compartilhados pelos elementos
Text
, você pode atribuir o modificador a uma variável local (por exemplo,centerWithPaddingModifier
) para o reutilizar. Isso é possível porque modificadores são objetos normais do Kotlin. - O
MaterialTheme
do Compose não tem uma correspondência exata para ocolorAccent
usado noplant_watering_header
. Por enquanto, useMaterialTheme.colors.primaryVariant
. Vamos melhorar isso na seção relevante.
Além disso, vamos conectar todas as partes e chamar PlantWatering
no PlantDetailContent
. O código XML ConstraintLayout que removemos no início tinha uma margem de 16.dp
que precisamos incluir no código do Compose.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/margin_normal">
Em PlantDetailContent
, crie uma Column
para mostrar o nome e as informações de irrigação e usar essas informações como padding. Além disso, para que as cores do plano de fundo e do texto sejam adequadas, adicione uma Surface
para processar essa informação.
PlantDetailDescription.kt
@Composable
fun PlantDetailContent(plant: Plant) {
Surface {
Column(Modifier.padding(dimensionResource(R.dimen.margin_normal))) {
PlantName(plant.name)
PlantWatering(plant.wateringInterval)
}
}
}
Se você atualizar a visualização, vai ver o seguinte:
9. Visualizações no código do Compose
Agora, vamos migrar a descrição da planta. O código em fragment_plant_detail.xml
tinha uma TextView
com app:renderHtml="@{viewModel.plant.description}"
para informar ao XML qual texto mostrar na tela. renderHtml
é um adaptador de vinculação que pode ser encontrado no arquivo PlantDetailBindingAdapters.kt
. A implementação usa HtmlCompat.fromHtml
para definir o texto na TextView
.
No entanto, o Compose não oferece suporte a classes Spanned
nem à exibição de texto formatado em HTML. Sendo assim, precisamos usar uma TextView
do sistema de visualização no código do Compose para ignorar essa limitação.
Como o Compose ainda não pode renderizar o código HTML, você vai criar uma TextView
de maneira programática para fazer isso usando a API AndroidView
.
A AndroidView
usa uma View
como parâmetro e fornece um callback para quando a visualização for inflada.
Para isso, vamos criar um novo elemento PlantDescription
que pode ser composto. Ele chama a AndroidView
com a TextView
que acabamos de lembrar em uma lambda. No callback factory
, inicialize uma TextView
que reaja às interações de HTML usando o Context
especificado. E, no callback update
, defina o texto com uma descrição formatada em 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.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")
}
}
Veja uma prévia:
Observe que a htmlDescription
se lembra da descrição em HTML de uma determinada description
transmitida como parâmetro. Se o parâmetro description
mudar, o código htmlDescription
dentro de remember
vai ser executado novamente.
Da mesma forma, o callback de atualização AndroidView
vai ser recomposto diante de uma mudança de htmlDescription
. Qualquer estado lido dentro do callback causa uma recomposição.
Vamos também adicionar uma PlantDescription
ao elemento PlantDetailContent
que pode ser composto e mudar o código de visualização para exibir uma descrição em 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)
}
}
Veja uma prévia
Neste ponto, você migrou todo o conteúdo do ConstraintLayout
original para o Compose. Execute o app para ver se ele está funcionando como esperado.
10. ViewCompositionStrategy
Por padrão, o Compose descarta a composição sempre que a ComposeView
se desanexa de uma janela. Isso não é desejável quando a ComposeView
é usada em fragmentos:
- A composição precisa seguir o ciclo de vida de visualização do fragmento para que os tipos de
View
da IU do Compose salvem o estado e - para manter os elementos da IU do Compose na tela quando houver transições neles ou em janelas. Durante as transições, a própria
ComposeView
permanece visível mesmo depois de ser desanexada da janela.
Você pode chamar o método AbstractComposeView.disposeComposition
manualmente para descartar a composição. Para descartar as composições automaticamente quando elas não forem mais necessárias, defina uma estratégia diferente ou crie uma própria chamando o método setViewCompositionStrategy
.
Use a estratégia DisposeOnViewTreeLifecycleDestroyed
para descartar a composição quando o LifecycleOwner
do fragmento for destruído.
Como o PlantDetailFragment
tem transições de entrada e saída (consulte nav_garden.xml
para ver mais informações), e como usaremos tipos de View
no Compose, precisamos garantir que a ComposeView
use a estratégia DisposeOnViewTreeLifecycleDestroyed
. No entanto, é uma prática recomendada definir essa estratégia ao usar a ComposeView
em fragmentos.
plantdetail/PlantDetailFragment.kt
import androidx.compose.ui.platform.ViewCompositionStrategy
...
class PlantDetailFragment : Fragment() {
...
override fun onCreateView(...): View? {
val binding = DataBindingUtil.inflate<FragmentPlantDetailBinding>(
inflater, 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. Aplicação de temas de interoperabilidade
Migramos o conteúdo de texto dos detalhes da planta para o Compose. No entanto, talvez você tenha percebido que o Compose não está usando as cores certas do tema. O nome da planta, que deveria estar verde, está roxo.
Neste estado inicial da migração, talvez você queira que o Compose herde os temas disponíveis no sistema de visualização em vez de reescrever do zero um tema próprio do Material Design. Os temas do Material Design funcionam perfeitamente com todos os componentes do Material Design no Compose.
Para reutilizar o tema Material Design Components (MDC) do sistema de visualização no Compose, use o compose-theme-adapter (link em inglês). A função MdcTheme
vai ler automaticamente o tema MDC do contexto do host e o transmitirá para o MaterialTheme
por você, tanto para temas claros quanto escuros. Embora você precise apenas das cores do tema neste codelab, a biblioteca também lê as formas e a tipografia do sistema de visualização.
A biblioteca já está incluída no arquivo app/build.gradle
:
...
dependencies {
...
implementation "com.google.android.material:compose-theme-adapter:$rootProject.composeVersion"
...
}
Para usá-la, substitua os usos de MaterialTheme
por MdcTheme
. Por exemplo, em PlantDetailFragment
:
PlantDetailFragment.kt
class PlantDetailFragment : Fragment() {
...
composeView.apply {
...
setContent {
MdcTheme {
PlantDetailDescription(plantDetailViewModel)
}
}
}
}
E todos os elementos da visualização no arquivo 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() {
MdcTheme {
PlantDescription("HTML<br><br>description")
}
}
Como é possível ver na visualização, o MdcTheme
está recebendo as cores do tema no arquivo styles.xml
.
Também é possível visualizar a IU no tema escuro criando uma nova função e transmitindo Configuration.UI_MODE_NIGHT_YES
ao 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)
}
}
Veja uma prévia
Se você executar o app, ele vai se comportar da mesma forma que antes da migração nos temas claro e escuro:
12. Teste
Depois de migrar partes da tela de detalhes da planta para o Compose, é fundamental fazer os testes para garantir que tudo esteja funcionando bem.
No app Sunflower, o PlantDetailFragmentTest
localizado na pasta androidTest
testa algumas funcionalidades do app. Abra o arquivo e dê uma olhada no código atual:
testPlantName
verifica o nome da planta na tela.testShareTextIntent
verifica se a intent correta é acionada após um toque no botão "Share" (compartilhar).
Quando uma atividade ou um fragmento usa o Compose, em vez de usar a ActivityScenarioRule
, você precisa usar a createAndroidComposeRule
, que integra a ActivityScenarioRule
com uma ComposeTestRule
que possibilita testar o código do Compose.
Em PlantDetailFragmentTest
, substitua o uso de ActivityScenarioRule
por createAndroidComposeRule
. Quando a regra de atividade for necessária para configurar o teste, use o atributo activityRule
da createAndroidComposeRule
desta maneira:
@RunWith(AndroidJUnit4::class)
class PlantDetailFragmentTest {
@Rule
@JvmField
val composeTestRule = createAndroidComposeRule<GardenActivity>()
...
@Before
fun jumpToPlantDetailFragment() {
populateDatabase()
composeTestRule.activityRule.scenario.onActivity { gardenActivity ->
activity = gardenActivity
val bundle = Bundle().apply { putString("plantId", "malus-pumila") }
findNavController(activity, R.id.nav_host).navigate(R.id.plant_detail_fragment, bundle)
}
}
...
}
Se você executar os testes, o testPlantName
vai falhar. A função testPlantName
verifica se há uma TextView na tela. No entanto, você migrou essa parte da IU para o Compose. Então, é necessário usar as declarações do Compose:
@Test
fun testPlantName() {
composeTestRule.onNodeWithText("Apple").assertIsDisplayed()
}
Se você executar os testes, vai ver que todos são aprovados.
13. Parabéns
Parabéns, você concluiu este codelab.
A ramificação compose
(link em inglês) do projeto original do app Sunflower no GitHub migra completamente a tela de detalhes da planta para o Compose. Além do que você fez neste codelab, ela também simula o comportamento do CollapsingToolbarLayout. Isso envolve:
- Carregar imagens com o Compose
- Animações
- Processamento de dimensões aprimorado
- E muito mais
A seguir
Confira os outros codelabs no programa de aprendizagem do Compose:
Leia mais
- Orientações sobre o código de migração do Jetpack Compose (vídeo em inglês)
- Guia de uso do Compose em apps existentes
- O Crane, um app de amostra (link em inglês), incorpora uma MapView no Compose