Adicionar uma lista rolável

1. Antes de começar

Neste codelab, você aprenderá a criar uma lista rolável no app usando o Jetpack Compose.

Você vai trabalhar com o app Affirmations, que mostra uma lista de frases inspiradoras combinadas com belas imagens para melhorar seu dia.

Os dados já estão disponíveis. Tudo o que você precisa fazer é mostrá-los na interface.

Pré-requisitos

  • Familiaridade com listas no Kotlin
  • Experiência em criação de layouts com o Jetpack Compose
  • Saber executar apps em um dispositivo ou emulador

O que você vai aprender

  • Como criar um card do Material Design usando o Jetpack Compose.
  • Como criar uma lista rolável usando o Jetpack Compose.

O que você vai criar

  • Partindo de um app já existente, você vai adicionar uma lista rolável à interface

O produto final ficará assim:

286f5132aa155fa6.png

O que é necessário

  • Um computador com acesso à Internet, um navegador da Web e o Android Studio
  • Acesso ao GitHub

Faça o download do código inicial

No Android Studio, abra a pasta basic-android-kotlin-compose-training-affirmations.

O esperado é que o app mostre uma tela em branco quando o código da ramificação starter for criado.

3beea0789e2eeaba.png

2. Criar uma classe de dados do item da lista

Criar uma classe de dados para uma afirmação

Em apps Android, as listas são compostas por itens. Para dados únicos, isso pode ser algo simples, como uma string ou um número inteiro. Para itens de lista com vários dados, como imagem e texto, você vai precisar de uma classe que contenha todas essas propriedades. As classes de dados são um tipo de classe que contém apenas propriedades e podem fornecer alguns métodos utilitários para trabalhar com essas propriedades.

  1. Crie um novo pacote em com.example.affirmations.

89c8d8485c685fac.png

Nomeie o novo pacote como model. O pacote de modelo vai conter o modelo de dados que será representado por uma classe de dados. A classe de dados será composta por propriedades que representam as informações relevantes para o que será uma "Afirmação", que consiste em um recurso de string e um de imagem. Os pacotes são diretórios que contêm classes e até mesmo outros diretórios.

b54fb6bf57de44c8.png

  1. Crie uma nova classe no pacote com.example.affirmations.model.

58510a651bd49100.png

Dê o nome Affirmation à nova classe e defina-a como uma Data class.

7f94b65ee3d8407f.png

  1. Cada Affirmation consiste em uma imagem e uma string. Crie duas propriedades val na classe de dados Affirmation. Uma delas precisa ter o nome stringResourceId e a outra, imageResourceId. Ambas precisam ser números inteiros.

Affirmation.kt

data class Affirmation(
    val stringResourceId: Int,
    val imageResourceId: Int
)
  1. Anote a propriedade stringResourceId com a anotação @StringRes e imageResourceId com a anotação @DrawableRes. O stringResourceId representa um ID para o texto da afirmação armazenado em um recurso de string. O imageResourceId representa um ID para a imagem da afirmação armazenada em um recurso drawable.

Affirmation.kt

import androidx.annotation.DrawableRes
import androidx.annotation.StringRes

data class Affirmation(
    @StringRes val stringResourceId: Int,
    @DrawableRes val imageResourceId: Int
)
  1. No pacote com.example.affirmations.data, abra o arquivo Datasource.kt e remova a marca de comentário das duas instruções de importação e do conteúdo da classe Datasource.

Datasource.kt

import com.example.affirmations.R
import com.example.affirmations.model.Affirmation

class Datasource() {
    fun loadAffirmations(): List<Affirmation> {
        return listOf<Affirmation>(
            Affirmation(R.string.affirmation1, R.drawable.image1),
            Affirmation(R.string.affirmation2, R.drawable.image2),
            Affirmation(R.string.affirmation3, R.drawable.image3),
            Affirmation(R.string.affirmation4, R.drawable.image4),
            Affirmation(R.string.affirmation5, R.drawable.image5),
            Affirmation(R.string.affirmation6, R.drawable.image6),
            Affirmation(R.string.affirmation7, R.drawable.image7),
            Affirmation(R.string.affirmation8, R.drawable.image8),
            Affirmation(R.string.affirmation9, R.drawable.image9),
            Affirmation(R.string.affirmation10, R.drawable.image10))
    }
}

3. Adicionar uma lista ao seu app

Criar um card de item da lista

O app precisa exibir uma lista de afirmações. A primeira etapa na configuração da interface para exibir uma lista é criar um item correspondente. Cada item da afirmação é composto por uma imagem e uma string. Os dados de cada um desses itens vêm com o código inicial, e você vai criar o componente de interface para mostrar cada item.

O item é um elemento Card combinável, que contém os elementos Image e Text. No Compose, Card é uma superfície que mostra conteúdo e ações em um único contêiner. O card de afirmação ficará assim na visualização:

4f657540712a069f.png

O card mostra uma imagem com texto abaixo. Esse layout vertical pode ser alcançado usando um elemento Column combinável encapsulado em um Card. Você pode fazer um teste ou seguir as etapas abaixo para fazer isso.

  1. Abrir o arquivo MainActivity.kt.
  2. Crie um novo método abaixo do método AffirmationsApp(), chamado AffirmationCard(), e adicione a anotação @Composable.

MainActivity.kt

@Composable
fun AffirmationsApp() {
}

@Composable
fun AffirmationCard() {

}
  1. Edite a assinatura do método para usar um objeto Affirmation como parâmetro. O objeto Affirmation vem do pacote model.

MainActivity.kt

import com.example.affirmations.model.Affirmation

@Composable
fun AffirmationCard(affirmation: Affirmation) {

}
  1. Adicione um parâmetro modifier à assinatura. Defina um valor padrão de Modifier para o parâmetro.

MainActivity.kt

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {

}
  1. No método AffirmationCard, chame o elemento Card combinável. Transmita o parâmetro modifier.

MainActivity.kt

import androidx.compose.material3.Card

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {

    }
}
  1. Adicione um elemento Column combinável ao Card. Os itens em um elemento Column combinável se organizam verticalmente na interface. Isso permite que você posicione uma imagem acima do texto associado. Por outro lado, uma Row combinável organiza os itens na horizontal.

MainActivity.kt

import androidx.compose.foundation.layout.Column

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {

        }
    }

}
  1. Adicione um elemento Image combinável ao corpo da lambda do Column. Não esqueça que uma Image combinável sempre exige um recurso para exibição e uma contentDescription. O recurso precisa ser um painterResource transmitido ao parâmetro painter. O método painterResource carregará drawables vetoriais ou formatos de recursos rasterizados, como PNGs. Além disso, transmita um stringResource para o parâmetro contentDescription.

MainActivity.kt

import androidx.compose.foundation.Image
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
            )
        }
    }
}
  1. Além dos parâmetros painter e contentDescription, transmita um modifier e um contentScale. Um contentScale determina como a imagem deve ser dimensionada e exibida. O objeto Modifier precisa ter o atributo fillMaxWidth definido e uma altura de 194.dp. contentScale precisa ser ContentScale.Crop.

MainActivity.kt

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.unit.dp
import androidx.compose.ui.layout.ContentScale

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
        }
    }
}
  1. Dentro da Column, crie um Text combinável após o elemento Image. Transmita um stringResource da affirmation.stringResourceId ao parâmetro text, transmita um objeto Modifier com o atributo padding definido como 16.dp e defina um tema de texto transmitindo MaterialTheme.typography.headlineSmall ao parâmetro style.

MainActivity.kt

import androidx.compose.material3.Text
import androidx.compose.foundation.layout.padding
import androidx.compose.ui.platform.LocalContext

@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
    Card(modifier = modifier) {
        Column {
            Image(
                painter = painterResource(affirmation.imageResourceId),
                contentDescription = stringResource(affirmation.stringResourceId),
                modifier = Modifier
                    .fillMaxWidth()
                    .height(194.dp),
                contentScale = ContentScale.Crop
            )
            Text(
                text = LocalContext.current.getString(affirmation.stringResourceId),
                modifier = Modifier.padding(16.dp),
                style = MaterialTheme.typography.headlineSmall
            )
        }
    }
}

Visualizar o elemento AffirmationCard combinável

Você trabalhou duro para criar o card, que é a parte principal da interface do app Affirmations. Para verificar se o card está correto, crie um elemento combinável que pode ser visualizado sem precisar abrir o app inteiro.

  1. Crie um método particular chamado AffirmationCardPreview(). Adicione as anotações @Preview e @Composable ao método.

MainActivity.kt

import androidx.compose.ui.tooling.preview.Preview

@Preview
@Composable
private fun AffirmationCardPreview() {

}
  1. No método, chame o elemento AffirmationCard combinável e transmita um novo objeto Affirmation com o recurso de string R.string.affirmation1 e o recurso drawable R.drawable.image1 transmitido ao construtor.

MainActivity.kt

@Preview
@Composable
private fun AffirmationCardPreview() {
    AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
  1. Abra a guia Split para visualizar o AffirmationCard. Se necessário, clique em Build & Refresh no painel Design para conferir a visualização.

924a4df2c1db236c.png

Criar a lista

O componente do item é o elemento fundamental da lista. Depois que o item da lista for criado, ele poderá ser usado para criar o componente da lista.

  1. Crie uma função com o nome AffirmationList(), adicione a anotação @Composable e declare uma List de objetos Affirmation como um parâmetro na assinatura do método.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {

}
  1. Declare um objeto modifier como um parâmetro na assinatura do método com um valor padrão de Modifier.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {

}
  1. No Jetpack Compose, uma lista rolável pode ser feita usando a função LazyColumn. A diferença entre uma LazyColumn e uma Column é que uma Column precisa ser usada quando há poucos itens para mostrar, já que o Compose carrega todos de uma só vez. Uma Column só pode conter um número predefinido ou fixo de elementos combináveis. Uma LazyColumn pode adicionar conteúdo sob demanda, o que é bom para listas longas, especialmente quando o tamanho da lista é desconhecido. Uma LazyColumn também oferece rolagem por padrão, sem precisar de outros códigos. Declare uma LazyColumn combinável dentro da função AffirmationList(). Transmita o objeto modifier como um argumento para a LazyColumn.

MainActivity.kt

import androidx.compose.foundation.lazy.LazyColumn

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {

    }
}
  1. No corpo da lambda da LazyColumn, chame o método items() e transmita a affirmationList. O método items() é como você adiciona itens à LazyColumn. Esse método é algo exclusivo desse elemento combinável e não é uma prática comum na maioria dos elementos.

MainActivity.kt

import androidx.compose.foundation.lazy.items

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) {

        }
    }
}
  1. Uma chamada para o método items() requer uma função lambda. Nessa função, especifique um parâmetro de affirmation que representa um item de afirmação da affirmationList.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->

        }
    }
}
  1. Para cada afirmação da lista, chame o elemento combinável AffirmationCard(). Transmita a affirmation e um objeto Modifier com o atributo padding definido como 8.dp.

MainActivity.kt

@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
    LazyColumn(modifier = modifier) {
        items(affirmationList) { affirmation ->
            AffirmationCard(
                affirmation = affirmation,
                modifier = Modifier.padding(8.dp)
            )
        }
    }
}

Mostrar a lista

  1. No elemento combinável AffirmationsApp, extraia as direções de layout atuais e salve-as em uma variável. Elas serão usadas para configurar o padding mais tarde.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
}
  1. Agora, crie um elemento combinável Surface. Esse elemento combinável vai definir o padding para o elemento combinável AffirmationsList.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface() {
    }
}
  1. Transmita um Modifier para o elemento combinável Surface que preencha a largura e a altura máximas do pai, defina o padding da barra de status, e o inicial e final como layoutDirection. Confira um exemplo de como converter um objeto LayoutDirection em padding: WindowInsets.safeDrawing.asPaddingValues().calculateStartPadding(layoutDirection).

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
    }
}
  1. Na lambda do elemento combinável Surface, chame o elemento AffirmationList e transmita DataSource().loadAffirmations() ao parâmetro affirmationList.

MainActivity.kt

import com.example.affirmations.data.Datasource

@Composable
fun AffirmationsApp() {
    val layoutDirection = LocalLayoutDirection.current
    Surface(
        Modifier = Modifier
        .fillMaxSize()
        .statusBarsPadding()
        .padding(
            start = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateStartPadding(layoutDirection),
            end = WindowInsets.safeDrawing.asPaddingValues()
                    .calculateEndPadding(layoutDirection),
        ),
    ) {
        AffirmationsList(
            affirmationList = Datasource().loadAffirmations(),
        )
    }
}

Execute o app Affirmations em um dispositivo ou emulador e confira o produto final.

286f5132aa155fa6.png

4. Acessar o código da solução

Para baixar o código do codelab concluído, use estes comandos git:

$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-affirmations.git
$ cd basic-android-kotlin-compose-training-affirmations
$ git checkout intermediate

Se preferir, você pode baixar o repositório como um arquivo ZIP, descompactar e abrir no Android Studio.

Se você quiser conferir o código da solução, acesse o GitHub (em inglês).

5. Conclusão

Agora você já sabe como criar cards, itens de lista e listas roláveis usando o Jetpack Compose. Essas são apenas ferramentas básicas para criar uma lista. Você pode soltar a criatividade e personalizar itens da lista como quiser.

Resumo

  • Use os elementos combináveis Card para criar itens de lista.
  • Modifique a interface contida em um elemento combináveis Card.
  • Crie uma lista rolável usando o elemento combinável LazyColumn.
  • Crie uma lista usando itens de lista personalizados.