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:
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.
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.
- Crie um novo pacote em com.example.affirmations.
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.
- Crie uma nova classe no pacote com.example.affirmations.model.
Dê o nome Affirmation à nova classe e defina-a como uma Data class.
- Cada
Affirmation
consiste em uma imagem e uma string. Crie duas propriedadesval
na classe de dadosAffirmation
. Uma delas precisa ter o nomestringResourceId
e a outra,imageResourceId
. Ambas precisam ser números inteiros.
Affirmation.kt
data class Affirmation(
val stringResourceId: Int,
val imageResourceId: Int
)
- Anote a propriedade
stringResourceId
com a anotação@StringRes
eimageResourceId
com a anotação@DrawableRes
. OstringResourceId
representa um ID para o texto da afirmação armazenado em um recurso de string. OimageResourceId
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
)
- 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:
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.
- Abrir o arquivo MainActivity.kt.
- Crie um novo método abaixo do método
AffirmationsApp()
, chamadoAffirmationCard()
, e adicione a anotação@Composable
.
MainActivity.kt
@Composable
fun AffirmationsApp() {
}
@Composable
fun AffirmationCard() {
}
- Edite a assinatura do método para usar um objeto
Affirmation
como parâmetro. O objetoAffirmation
vem do pacotemodel
.
MainActivity.kt
import com.example.affirmations.model.Affirmation
@Composable
fun AffirmationCard(affirmation: Affirmation) {
}
- Adicione um parâmetro
modifier
à assinatura. Defina um valor padrão deModifier
para o parâmetro.
MainActivity.kt
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
}
- No método
AffirmationCard
, chame o elementoCard
combinável. Transmita o parâmetromodifier
.
MainActivity.kt
import androidx.compose.material3.Card
@Composable
fun AffirmationCard(affirmation: Affirmation, modifier: Modifier = Modifier) {
Card(modifier = modifier) {
}
}
- Adicione um elemento
Column
combinável aoCard
. Os itens em um elementoColumn
combinável se organizam verticalmente na interface. Isso permite que você posicione uma imagem acima do texto associado. Por outro lado, umaRow
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 {
}
}
}
- Adicione um elemento
Image
combinável ao corpo da lambda doColumn
. Não esqueça que umaImage
combinável sempre exige um recurso para exibição e umacontentDescription
. O recurso precisa ser umpainterResource
transmitido ao parâmetropainter
. O métodopainterResource
carregará drawables vetoriais ou formatos de recursos rasterizados, como PNGs. Além disso, transmita umstringResource
para o parâmetrocontentDescription
.
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),
)
}
}
}
- Além dos parâmetros
painter
econtentDescription
, transmita ummodifier
e umcontentScale
. UmcontentScale
determina como a imagem deve ser dimensionada e exibida. O objetoModifier
precisa ter o atributofillMaxWidth
definido e uma altura de194.dp
.contentScale
precisa serContentScale.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
)
}
}
}
- Dentro da
Column
, crie umText
combinável após o elementoImage
. Transmita umstringResource
daaffirmation.stringResourceId
ao parâmetrotext
, transmita um objetoModifier
com o atributopadding
definido como16.dp
e defina um tema de texto transmitindoMaterialTheme.typography.headlineSmall
ao parâmetrostyle
.
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.
- 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() {
}
- No método, chame o elemento
AffirmationCard
combinável e transmita um novo objetoAffirmation
com o recurso de stringR.string.affirmation1
e o recurso drawableR.drawable.image1
transmitido ao construtor.
MainActivity.kt
@Preview
@Composable
private fun AffirmationCardPreview() {
AffirmationCard(Affirmation(R.string.affirmation1, R.drawable.image1))
}
- Abra a guia Split para visualizar o
AffirmationCard
. Se necessário, clique em Build & Refresh no painel Design para conferir a visualização.
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.
- Crie uma função com o nome
AffirmationList()
, adicione a anotação@Composable
e declare umaList
de objetosAffirmation
como um parâmetro na assinatura do método.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>) {
}
- Declare um objeto
modifier
como um parâmetro na assinatura do método com um valor padrão deModifier
.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
}
- No Jetpack Compose, uma lista rolável pode ser feita usando a função
LazyColumn
. A diferença entre umaLazyColumn
e umaColumn
é que umaColumn
precisa ser usada quando há poucos itens para mostrar, já que o Compose carrega todos de uma só vez. UmaColumn
só pode conter um número predefinido ou fixo de elementos combináveis. UmaLazyColumn
pode adicionar conteúdo sob demanda, o que é bom para listas longas, especialmente quando o tamanho da lista é desconhecido. UmaLazyColumn
também oferece rolagem por padrão, sem precisar de outros códigos. Declare umaLazyColumn
combinável dentro da funçãoAffirmationList()
. Transmita o objetomodifier
como um argumento para aLazyColumn
.
MainActivity.kt
import androidx.compose.foundation.lazy.LazyColumn
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
}
}
- No corpo da lambda da
LazyColumn
, chame o métodoitems()
e transmita aaffirmationList
. O métodoitems()
é 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) {
}
}
}
- Uma chamada para o método
items()
requer uma função lambda. Nessa função, especifique um parâmetro deaffirmation
que representa um item de afirmação daaffirmationList
.
MainActivity.kt
@Composable
fun AffirmationList(affirmationList: List<Affirmation>, modifier: Modifier = Modifier) {
LazyColumn(modifier = modifier) {
items(affirmationList) { affirmation ->
}
}
}
- Para cada afirmação da lista, chame o elemento combinável
AffirmationCard()
. Transmita aaffirmation
e um objetoModifier
com o atributopadding
definido como8.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
- 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
}
- Agora, crie um elemento combinável
Surface
. Esse elemento combinável vai definir o padding para o elemento combinávelAffirmationsList
.
MainActivity.kt
import com.example.affirmations.data.Datasource
@Composable
fun AffirmationsApp() {
val layoutDirection = LocalLayoutDirection.current
Surface() {
}
}
- Transmita um
Modifier
para o elemento combinávelSurface
que preencha a largura e a altura máximas do pai, defina o padding da barra de status, e o inicial e final comolayoutDirection
. Confira um exemplo de como converter um objetoLayoutDirection
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),
),
) {
}
}
- Na lambda do elemento combinável
Surface
, chame o elementoAffirmationList
e transmitaDataSource().loadAffirmations()
ao parâmetroaffirmationList
.
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.
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.