1. Antes de comenzar
Desde el comienzo, Jetpack Compose se diseñó con interoperabilidad con View, es decir que Compose y el sistema de vistas pueden compartir recursos y funcionar en conjunto para mostrar la IU. Esta funcionalidad te permite agregar Compose a tu app existente basada en objetos View. Es decir, los elementos Compose y View pueden coexistir en tu base de código hasta que toda tu app esté hecha por completo con Compose.
En este codelab, pasarás a Compose el elemento de lista hecho con vistas de la app de Juice Tracker. Si lo deseas, puedes convertir el resto de las vistas de Juice Tracker por tu cuenta.
Si tienes una app con una IU basada en objetos View, es posible que no quieras volver a escribir toda su IU de una sola vez. Este codelab te ayudará a convertir un elemento View independiente de una IU que se basa en esos objetos a un elemento Compose.
Requisitos previos
- Conocimientos básicos de la IU basada en objetos View
- Conocimientos para compilar una app con una IU basada en objetos View
- Experiencia con la sintaxis de Kotlin, incluidas las funciones de lambdas
- Conocimientos para compilar una app en Jetpack Compose
Qué aprenderás
- Cómo agregar Compose a una pantalla existente que se compiló con vistas de Android
- Cómo obtener una vista previa de un elemento componible agregado a tu app basada en objetos View
Qué compilarás
- Convertirás un elemento de lista basado en objetos View a Compose en la app de Juice Tracker.
2. Descripción general de la app de partida
En este codelab, se usa el código de solución de la app de Juice Tracker que se creó en Cómo compilar una app para Android con vistas como código de partida. La app de partida ya guarda datos con la biblioteca de persistencias Room. El usuario puede agregar información de jugos a la base de datos de la app, como el nombre del jugo, una descripción, un color y una calificación.
En este codelab, convertirás a Compose el elemento de lista basado en objetos View.
Descarga el código de partida para este codelab
Para comenzar, descarga el código de partida:
Como alternativa, puedes clonar el repositorio de GitHub para el código:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views
Puedes explorar el código en el repositorio de GitHub de JuiceTracker
.
3. Agrega la biblioteca de Jetpack Compose
Los objetos Recollect, Compose y View pueden coexistir en una pantalla determinada. Puedes tener algunos elementos de la IU en Compose y otros en el sistema de View. Por ejemplo, puedes tener solo la lista en Compose, mientras que el resto de la pantalla está en el sistema de View.
Completa los siguientes pasos para agregar la biblioteca de Compose a la app de Juice Tracker.
- Abre Juice Tracker en Android Studio.
- Abre
build.gradle.kts
al nivel de la app. - Dentro del bloque
buildFeatures
, agrega una marcacompose = true
.
buildFeatures {
//...
// Enable Jetpack Compose for this module
compose = true
}
Esta marca permite que Android Studio funcione con Compose. No creaste este código en los codelabs anteriores porque Android Studio lo genera automáticamente cuando creas un nuevo proyecto de plantilla con Compose.
- Debajo de
buildFeatures
, agrega el bloquecomposeOptions
. - Dentro del bloque, establece
kotlinCompilerExtensionVersion
en"1.5.1"
para establecer la versión del compilador de Kotlin.
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
- En la sección
dependencies
, agrega las dependencias de Compose. Necesitas las siguientes dependencias para agregar Compose a una app basada en View. Estas dependencias ayudan a integrar Compose con Activity, agregar la biblioteca de componentes de diseño de Compose, admitir temas de Compose Jetpack y proporcionar herramientas para brindar una mejor compatibilidad con IDE.
dependencies {
implementation(platform("androidx.compose:compose-bom:2023.06.01"))
// other dependencies
// Compose
implementation("androidx.activity:activity-compose:1.7.2")
implementation("androidx.compose.material3:material3")
implementation("com.google.accompanist:accompanist-themeadapter-material3:0.28.0")
debugImplementation("androidx.compose.ui:ui-tooling")
}
Agrega un objeto ComposeView
Un objeto ComposeView
es una vista de Android que puede alojar contenido de la IU de Jetpack Compose. Usa setContent
para proporcionar la función de componibilidad de contenido de la vista.
- Abre el archivo
layout/list_item.xml
y obtén una vista previa en la pestaña Split.
Al final de este codelab, reemplazarás el objeto View por un elemento componible.
- En
JuiceListAdapter.kt
, quitaListItemBinding
de todas partes. En la claseJuiceListViewHolder
, reemplazabinding.root
porcomposeView
.
import androidx.compose.ui.platform.ComposeView
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
): RecyclerView.ViewHolder(composeView)
- En la carpeta
onCreateViewHolder()
, actualiza la funciónreturn()
para que coincida con el siguiente código:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): JuiceListViewHolder {
return JuiceListViewHolder(
ComposeView(parent.context),
onEdit,
onDelete
)
}
- En la clase
JuiceListViewHolder
, borra todas las variablesprivate
y quita todo el código de la funciónbind()
. La claseJuiceListViewHolder
ahora luce como el siguiente código:
class JuiceListViewHolder(
private val onEdit: (Juice) -> Unit,
private val onDelete: (Juice) -> Unit
) : RecyclerView.ViewHolder(composeView) {
fun bind(juice: Juice) {
}
}
- En este momento, puedes borrar las importaciones
com.example.juicetracker.databinding.ListItemBinding
yandroid.view.LayoutInflater
.
// Delete
import com.example.juicetracker.databinding.ListItemBinding
import android.view.LayoutInflater
- Borra el archivo
layout/list_item.xml
. - Selecciona OK en el cuadro de diálogo Delete.
4. Agrega una función de componibilidad
A continuación, crearás un elemento componible que emite el elemento de la lista. El elemento componible toma Juice
y dos funciones de devolución de llamada para editar y borrar el elemento de la lista.
- En
JuiceListAdapter.kt
, después de la definición de la claseJuiceListAdapter
, crea una función de componibilidad llamadaListItem()
. - Haz que la función
ListItem()
acepte el objetoJuice
y una devolución de llamada lambda para borrar.
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@Composable
fun ListItem(
input: Juice,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
Echa un vistazo a la vista previa del elemento de la lista que deseas crear. Observa que tiene un ícono de jugo, detalles del jugo y un ícono de botón para borrar. En breve, implementarás estos componentes.
Cómo crear un ícono componible de Juice
- En
JuiceListAdapter.kt
, después del elementoListItem()
componible, crea otra función de componibilidad llamadaJuiceIcon()
que tome uncolor
y unModifier
.
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
}
- Dentro de la función
JuiceIcon()
, agrega variables decolor
y la descripción del contenido como se muestra en el siguiente código:
@Composable
fun JuiceIcon(color: String, modifier: Modifier = Modifier) {
val colorLabelMap = JuiceColor.values().associateBy { stringResource(it.label) }
val selectedColor = colorLabelMap[color]?.let { Color(it.color) }
val juiceIconContentDescription = stringResource(R.string.juice_color, color)
}
Con las variables colorLabelMap
y selectedColor
, recuperarás el recurso de color asociado con la selección del usuario.
- Agrega un diseño
Box
para mostrar un íconoic_juice_color
y un íconoic_juice_clear
, uno encima del otro. El íconoic_juice_color
tiene un tono y está alineado con el centro.
import androidx.compose.foundation.layout.Box
Box(
modifier.semantics {
contentDescription = juiceIconContentDescription
}
) {
Icon(
painter = painterResource(R.drawable.ic_juice_color),
contentDescription = null,
tint = selectedColor ?: Color.Red,
modifier = Modifier.align(Alignment.Center)
)
Icon(painter = painterResource(R.drawable.ic_juice_clear), contentDescription = null)
}
Como ya tienes conocimientos sobre implementación de elementos componibles, no se proporcionan detalles de cómo se hace.
- Agrega una función para obtener una vista previa de
JuiceIcon()
. Pasa el color comoYellow
.
import androidx.compose.ui.tooling.preview.Preview
@Preview
@Composable
fun PreviewJuiceIcon() {
JuiceIcon("Yellow")
}
Crea detalles de Juice componibles
En JuiceListAdapter.kt
, debes agregar otra función de componibilidad para mostrar los detalles de los jugos. También necesitas un diseño de columna para mostrar dos elementos componibles Text
de nombre y descripción, además de un indicador de calificación. Para ello, completa los siguientes pasos:
- Agrega una función de componibilidad llamada
JuiceDetails()
que tome un objetoJuice
y unModifier
, un texto componible para el nombre del jugo y un elemento componible para la descripción del jugo, como se muestra en el siguiente código:
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.text.font.FontWeight
@Composable
fun JuiceDetails(juice: Juice, modifier: Modifier = Modifier) {
Column(modifier, verticalArrangement = Arrangement.Top) {
Text(
text = juice.name,
style = MaterialTheme.typography.h5.copy(fontWeight = FontWeight.Bold),
)
Text(juice.description)
RatingDisplay(rating = juice.rating, modifier = Modifier.padding(top = 8.dp))
}
}
- Para resolver el error de referencia sin resolver, crea una función de componibilidad llamada
RatingDisplay()
.
En el sistema de vistas, tienes un elemento RatingBar
para mostrar la siguiente barra de calificación. Compose no tiene una barra de calificación componible, por lo que debes implementar este elemento desde cero.
- Define la función
RatingDisplay()
para mostrar las estrellas según la calificación. Esta función de componibilidad muestra la cantidad de estrellas según la calificación.
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
@Composable
fun RatingDisplay(rating: Int, modifier: Modifier = Modifier) {
val displayDescription = pluralStringResource(R.plurals.number_of_stars, count = rating)
Row(
// Content description is added here to support accessibility
modifier.semantics {
contentDescription = displayDescription
}
) {
repeat(rating) {
// Star [contentDescription] is null as the image is for illustrative purpose
Image(
modifier = Modifier.size(32.dp),
painter = painterResource(R.drawable.star),
contentDescription = null
)
}
}
}
Para crear el elemento de diseño de estrella en Compose, debes crear el recurso vectorial de estrella.
- En el panel Project, haz clic con el botón derecho en drawable > New > Vector Asset.
- En el diálogo Asset Studio, busca el ícono de estrella. Selecciona el ícono de estrella con relleno.
- Cambia el valor de color de la estrella a 625B71.
- Haz clic en Next > Finish.
- Observa que aparece un elemento de diseño en la carpeta
res/drawable
.
- Agrega un elemento componible de vista previa para obtener una vista previa del elemento componible
JuiceDetails
.
@Preview
@Composable
fun PreviewJuiceDetails() {
JuiceDetails(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4))
}
Crea un elemento componible de botón Borrar
- En
JuiceListAdapter.kt
, agrega otra función de componibilidad llamadaDeleteButton()
que tome una función de devolución de llamada lambda y un modificador. - Configura la lambda para el argumento
onClick
y pasa elIcon()
como se muestra en el siguiente código:
import androidx.compose.ui.res.painterResource
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@Composable
fun DeleteButton(onDelete: () -> Unit, modifier: Modifier = Modifier) {
IconButton(
onClick = { onDelete() },
modifier = modifier
) {
Icon(
painter = painterResource(R.drawable.ic_delete),
contentDescription = stringResource(R.string.delete)
)
}
}
- Agrega una función de vista previa para obtener una vista previa del botón Borrar.
@Preview
@Composable
fun PreviewDeleteIcon() {
DeleteButton({})
}
5. Implementa la función ListItem
Ahora que tienes todos los elementos componibles necesarios para mostrar el elemento de la lista, puedes organizarlos en un diseño. Observa la función ListItem()
que definiste en el paso anterior.
@Composable
fun ListItem(
input: Juice,
onEdit: (Juice) -> Unit,
onDelete: (Juice) -> Unit,
modifier: Modifier = Modifier
) {
}
En JuiceListAdapter.kt
, completa los siguientes pasos para implementar la función ListItem()
.
- Agrega un diseño
Row
dentro de la lambdaMdc3Theme {}
.
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import com.google.accompanist.themeadapter.material3.Mdc3Theme
Mdc3Theme {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.SpaceBetween
) {
}
}
- Dentro de la lambda
Row
, llama a los tres elementosJuiceIcon
,JuiceDetails
yDeleteButton
componibles que creaste como elementos secundarios.
JuiceIcon(input.color)
JuiceDetails(input, Modifier.weight(1f))
DeleteButton({})
Pasar el Modifier.
weight
(1f)
al elemento JuiceDetails()
componible garantiza que los detalles del jugo ocupen el espacio horizontal restante después de medir los elementos secundarios no ponderados
- Pasa el modificador y la lambda
onDelete(input)
con la alineación superior como parámetros al elemento componibleDeleteButton
.
DeleteButton(
onDelete = {
onDelete(input)
},
modifier = Modifier.align(Alignment.Top)
)
- Escribe una función de vista previa para obtener una vista previa del elemento
ListItem
componible.
@Preview
@Composable
fun PreviewListItem() {
ListItem(Juice(1, "Sweet Beet", "Apple, carrot, beet, and lemon", "Red", 4), {})
}
- Vincula el elemento
ListItem
componible con el contenedor de vistas. Llama aonEdit(input)
dentro de la función lambdaclickable()
para abrir el diálogo de edición cuando se hace clic en el elemento de la lista.
En la clase JuiceListViewHolder
, dentro de la función bind()
, debes alojar el elemento componible. Usas ComposeView
, que es una vista de Android que puede alojar contenido de la IU de Compose con el método setContent
.
fun bind(input: Juice) {
composeView.setContent {
ListItem(
input,
onDelete,
modifier = Modifier
.fillMaxWidth()
.clickable {
onEdit(input)
}
.padding(vertical = 8.dp, horizontal = 16.dp),
)
}
}
- Ejecuta la app. Agrega tu jugo favorito. Observa el elemento brillante de la lista de Compose.
.
¡Felicitaciones! Acabas de crear tu primera app de interoperabilidad con Compose que usa elementos de Compose en una app basada en objetos View.
6. Obtén el código de la solución
Para descargar el código del codelab terminado, puedes usar estos comandos de git:
$ git clone https://github.com/google-developer-training/basic-android-kotlin-compose-training-juice-tracker.git $ cd basic-android-kotlin-compose-training-juice-tracker $ git checkout views-with-compose
También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.
Si deseas ver el código de la solución, puedes hacerlo en GitHub.
7. Más información
Documentación para desarrolladores de Android
- Herramientas para Compose | Jetpack Compose | Android Developers
- APIs de interoperabilidad| Jetpack Compose | Android Developers
- Estrategia de migración | Jetpack Compose | Android Developers
Codelab [Intermedio]