Las Tooltips muestran información sobre otro elemento con el fin de comunicar al usuario sobre su propósito.
Existen dos tipos:
- Plain tooltip: Usada para descripciones cortas (comúnmente en action buttons)
- Rich tooltip: Usada para descripciones con más detalle e incluso acciones asociadas
Dicho la anterior, en este tutorial veremos cómo crear ambos tipos de tooltip con la librería Jetpack Compose de Android y como leer los eventos del usuario para su aparición en pantalla.
Proyecto De Tooltips De Android Studio
1. Para estudiar el uso de las tooltips, crea un nuevo proyecto en Android Studio con la plantilla de Jetpack Compose y nombralo «Tooltips».
2. Luego crea un archivo Kotlin llamado TooltipsScreen.kt
y añade una función componible con el mismo nombre. Esta representará el diseño de la pantalla donde mostraremos nuestros ejemplos.
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
@Composable
fun TooltipsScreen() {
}
@Preview
@Composable
private fun Preview() {
TootipsTheme {
TooltipsScreen()
}
}
3. Abre MainActivity.kt
y reemplaza el contenido de setContent()
por la invocación de la función que añadimos en el paso anterior:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TootipsTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
TooltipsScreen()
}
}
}
}
}
Por otro lado, si deseas ver el código final de inmediato o tuviste problemas siguiendo las explicaciones, puedes encontrar el proyecto completo en mi repositorio de GitHub:
1. Plain Tooltip
El primer ejemplo que veremos será el uso de una Plain Tooltip sobre un Floating Action Button como se presenta en la ilustración anterior.
El FAB representa una acción de creación, por lo que nuestro objetivo es mostrar una etiqueta que describa dicha función cuando el usuario haga un Long Click.
Con esto en mente, veamos cómo hacerlo.
Crear Floating Action Button
Iniciemos declarando un elemento Scaffold en TooltipsScreen()
y añadiéndole el FAB que necesitamos:
@Composable
fun TooltipsScreen() {
Scaffold(
topBar = { TopBar() },
floatingActionButton = { Fab() },
content = { padding -> MainContent(padding) }
)
}
Crea la función Fab()
en un nuevo archivo Fab.kt
e invoca a LargeFloatingActionButton()
con el icono y descripción de creación:
@Composable
fun Fab() {
LargeFloatingActionButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Crear"
)
}
}
Crear Tooltip Box
Tanto las plain tooltips como las rich tooltips usan al componente TooltipBox()
para organizar la superposición de la tooltip con el elemento a describir:
@Composable
fun TooltipBox(
positionProvider: PopupPositionProvider,
tooltip: @Composable () -> Unit,
state: TooltipState,
modifier: Modifier = Modifier,
focusable: Boolean = true,
enableUserInput: Boolean = true,
content: @Composable () -> Unit
): Unit
Con este componente evitamos preocuparnos por el posicionamiento y las especificaciones estándar de la tooltip. E incluso maneja el evento de Long Press del usuario y oculta la tooltip luego de un tiempo.
Continuando, recubre al FAB con TooltipBox()
y pasa como argumentos los siguientes valores:
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.LargeFloatingActionButton
import androidx.compose.material3.PlainTooltip
import androidx.compose.material3.Text
import androidx.compose.material3.TooltipBox
import androidx.compose.material3.TooltipDefaults
import androidx.compose.material3.rememberTooltipState
import androidx.compose.runtime.Composable
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun Fab() {
TooltipBox(
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
tooltip = { },
state = rememberTooltipState()
) {
LargeFloatingActionButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = "Crear"
)
}
}
}
Del código anterior:
positionProvider
: Elemento usado para posicionar de la tooltip con respecto a content. Usamos la utilidadrememberPlaintTooltipPositionProvider()
para que el framework ubique correctamente la tootip simpletooltip
: Aquí irá la tooltip que veremos a continuaciónstate
: Estado de la tooltip. La funciónrememberTooltipState()
crea y recuerda un valor por defectocontent
: Trailing lambda con parámetro componible para ubicar nuestro Fab
Crear Plain Tooltip
La pequeña superficie con la descripción de la plain tooltip es producida por la función componible PlainTooltip()
:
@Composable
fun PlainTooltip(
modifier: Modifier = Modifier,
contentColor: Color = TooltipDefaults.plainTooltipContentColor,
containerColor: Color = TooltipDefaults.plainTooltipContainerColor,
shape: Shape = TooltipDefaults.plainTooltipContainerShape,
content: @Composable () -> Unit
): Unit
En su invocación solo necesitamos el uso de content
para pasar el texto de la etiqueta a renderizar. Por lo que pasaremos un texto sin más:
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun Fab() {
TooltipBox(
tooltip = { PlainTooltip { Text("Crear") } },
//..
) {
//..
}
}
Ahora corre el proyecto y prueba haciendo un click prolongado sobre el FAB para visualizar la etiqueta:
2. Rich Tooltip
En el segundo ejemplo desplegaremos una Rich Tooltip sobre un action button de una Top App Bar, al igual que en la ilustración anterior.
Este caso muestra como este tipo de tooltip puede informar con mayor detalle el objetivo de un componente de UI, e incluso proveer una acción asociada para ampliar aún más el detalle (esto lo veremos en el ejemplo de la persistent rich tooltip).
Crear Top App Bar
Crea un nuevo archivo Kotlin llamado TopBar.kt
y añade la función TopBar()
. En su interior invoca a CenterAlignedTopAppBar()
para materializar una barra con título centrado. Añádele una acción para ajustes:
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun TopBar() {
CenterAlignedTopAppBar(
title = { AppBarTitle() },
actions = { SettingsAction() }
)
}
AppBarTitle()
representa al texto de la top bar y SettingsAction()
contiene la cobertura de la rich tooltip sobre el icon button de ajustes.
@Composable
private fun AppBarTitle() {
Text(stringResource(R.string.app_name))
}
Crear Toolptip Box
Ya que el Icon Button será el elemento a describir, lo envolvemos con TooltipBox()
:
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun SettingsAction() {
TooltipBox(
positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
tooltip = { SettingsTooltip() },
state = rememberTooltipState(),
content = { SettingsIconButton() }
)
}
@Composable
private fun SettingsIconButton() {
IconButton(onClick = {}) {
Icon(
imageVector = Icons.Default.Settings,
contentDescription = "Ajustes"
)
}
}
Y similar a la plain tooltip, existe la función de utilidad rememberRichTooltipPositionProvider()
, cuyo objetivo es ubicar correctamente la rich tooltip.
Crear Rich Tooltip
Ahora en el slot para la tooltip
invocaremos a la función RichTooltip()
:
@Composable
fun RichTooltip(
modifier: Modifier = Modifier,
title: (@Composable () -> Unit)? = null,
action: (@Composable () -> Unit)? = null,
colors: RichTooltipColors = TooltipDefaults.richTooltipColors(),
shape: Shape = TooltipDefaults.richTooltipContainerShape,
text: @Composable () -> Unit
): Unit
En su firma tendremos los elementos de la anatomía:
title
: Títulotext
: Texto de apoyoaction
: Las acciones a usar
Por lo que la construimos con los siguientes argumentos:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SettingsTooltip() {
RichTooltip(
title = { Text("Ajustes") },
text = { Text("Preferencias de tu cuenta y comportamiento de la App") },
)
}
Mostrar Rich Tooltip
De nuevo, la aparición y el evento de long press los maneja la tooltip box, así solo resta correr el proyecto y comprobar que aparezca la rich tooltip al interactuar con el icono de ajustes en la Top App Bar.
3. Persistent Rich Tooltip
El comportamiento por defecto de las tooltips es desaparecer luego de alejarse del área del componente de UI descrito o cuando aparece otra tooltip.
No obstante, los lineamientos de M3 promueven el uso de Rich tooltips persistentes, las cuales se mantiene visibles hasta que se interactúe con otro elemento de UI o con la acción de la misma.
Estas ya no aparecen por un evento de long press, si no, al hacer tap sobre el elemento de UI; o automáticamente para introducir una nueva característica de la App.
Para este caso veremos un ejemplo en el que al iniciar el aplicativo, se presenta un texto con la cantidad de tareas pendientes, como una nueva característica añadida.
Pasemos a implementarlo.
Crear Contenido Del Scaffold
La sección rectangular que se observa en la ilustración del ejemplo es representada por una columna con un texto en su interior. Esta será ubicada en el contenido principal del Scaffold. Así que crearemos en un archivo nuevo llamado MainContent.kt
y la función respectiva:
@Composable
fun MainContent(
padding: PaddingValues
) {
Box(
modifier = Modifier
.padding(padding)
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
NewFeaturePanel()
}
}
La función NewFeaturePanel()
será donde pondremos el panel de tareas pendientes y lo recubriremos con la tooltip box persistente.
Crear Persistent Rich Tooltip
Al interior de NewFeaturePanel()
declaramos el estado personalizado para una rich tooltip persistente y se lo pasamos a la invocación de TooltipBox()
:
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun NewFeaturePanel() {
val state = rememberTooltipState(
initialIsVisible = true,
isPersistent = true
)
TooltipBox(
positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
tooltip = { PersistentRichTooltip(state) },
enableUserInput = false,
state = state,
content = { PendingTasksInfo(state.isVisible) }
)
}
La configuración de nuestra tooltip persistente se basa en aplicar:
initialIsVisible
-> Usatrue
para mostrar la tooltip automáticamenteisPersistent
-> Usatrue
para marcar persistencia de la tooltipenableUserInput
-> Usafalse
para evitar procesar el long press del usuario
Acto seguido, pasamos a definir la rich tooltip en PersistentRichTooltip()
con los valores en la ilustración del ejemplo:
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun PersistentRichTooltip(state: TooltipState) {
RichTooltip(
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
NewReleaseIcon()
Spacer(Modifier.size(4.dp))
Text("Nueva característica disponible")
}
},
action = {
TextButton(onClick = { state.dismiss() }) {
Text("Lo tengo")
}
},
text = {
Text(
"Ahora puedes ver la cantidad de tareas pendientes en " +
"este panel para mantenerte al tanto de tu progreso"
)
}
)
}
La función recibe el estado como parámetro, el cual nos permitirá ocultar la tooltip cuando el Text Button sea presionado.
Efecto que se logra invocando a TooltipState.dismiss()
en onClick
(Usa show()
cuando desees mostrar una tooltip persistente por evento de click)
Cambiar Bakground De Panel
Con la función PendingTaskInfo()
defineremos el diseño del panel:
@Composable
private fun PendingTasksInfo(isFocused: Boolean) {
Column(
Modifier
.width(300.dp)
.height(56.dp)
) {
Text(
text = "Cantidad de tareas pendientes: 10",
modifier = Modifier
.background(color = pendingTasksInfoColor(isFocused))
.border(
width = Dp.Hairline,
color = Color.LightGray,
shape = RoundedCornerShape(
4.dp
)
)
.padding(8.dp)
.fillMaxWidth()
)
}
}
@Composable
private fun pendingTasksInfoColor(isFocused: Boolean): Color {
return if (isFocused)
MaterialTheme.colorScheme.primary.copy(alpha = 0.08f)
else
MaterialTheme.colorScheme.surface
}
El color del background en el panel es determinado por la visibilidad de la tooltip, por eso se usa el valor de la propiedad TooltipState.isVisible
como argumento de PendingTaskInfo()
.
Finaliza esta lectura ejecutando el proyecto Android Studio y evidencia como la Rich Tooltip aparece al iniciar el aplicativo. Además de no ocultarse a menos que presiones el botón o en un área por fuera de la tooltip.
Únete Al Discord De Develou
Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!