En esta página, se describe cómo manejar los tamaños y brindar respuestas flexibles y responsivas diseños con Glance, usando los componentes existentes de Glance)
Usa Box
, Column
y Row
Vistazo tiene tres diseños principales componibles:
Box
: Coloca los elementos encima de otros. Se traduce en unRelativeLayout
.Column
: Ubica los elementos uno detrás de otro en el eje vertical. Traduce a un objetoLinearLayout
con orientación vertical.Row
: Ubica los elementos uno detrás de otro en el eje horizontal. Traduce a un objetoLinearLayout
con orientación horizontal.
Vistazo compatible con objetos Scaffold
. Coloca tu Column
, Row
y
Elementos Box
componibles dentro de un objeto Scaffold
determinado
Cada uno de estos elementos componibles te permite definir las alineaciones verticales y horizontales. de su contenido y las restricciones de ancho, alto, peso o relleno con modificadores. Además, cada elemento secundario puede definir su modificador para cambiar el espacio y su ubicación dentro del elemento superior.
En el siguiente ejemplo, se muestra cómo crear un Row
que se distribuya de manera uniforme
horizontalmente, como se ve en la Figura 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
El elemento Row
ocupa el ancho máximo disponible y, debido a que cada elemento secundario tiene el mismo
el peso, comparten de manera uniforme
el espacio disponible. Puedes definir diferentes pesos,
tamaños, rellenos o alineaciones para adaptar los diseños a tus necesidades.
Cómo usar diseños desplazables
Otra forma de proporcionar contenido responsivo es hacerlo que se pueda desplazar. Este es
posible con el elemento LazyColumn
componible. Este elemento componible te permite definir un conjunto
de elementos que se mostrarán dentro de un contenedor desplazable en el widget de la app.
Los siguientes fragmentos muestran distintas maneras de definir elementos dentro del
LazyColumn
Puedes proporcionar la cantidad de elementos de las siguientes maneras:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Proporciona elementos individuales:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Proporciona una lista o un array de elementos:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
También puedes usar una combinación de los ejemplos anteriores:
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
Ten en cuenta que el fragmento anterior no especifica el itemId
. Especificar el valor
itemId
ayuda a mejorar el rendimiento y mantener el desplazamiento.
de posición a través de la lista y actualizaciones de appWidget
a partir de Android 12 (para
por ejemplo, cuando se agregan o quitan elementos de la lista). El siguiente ejemplo
se muestra cómo especificar un itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
Define el SizeMode
Los tamaños de AppWidget
pueden variar según el dispositivo, la elección del usuario o el selector.
por lo que es importante proporcionar diseños flexibles como se describe en la sección Cómo proporcionar
de widgets flexibles. La vista rápida simplifica esto con el SizeMode
definición y el valor LocalSize
. En las siguientes secciones, se describen los tres
modos.
SizeMode.Single
SizeMode.Single
es el modo predeterminado. Indica que solo un tipo de
se proporciona contenido. es decir, incluso si cambia el tamaño disponible de AppWidget
,
no se cambia el tamaño del contenido.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
Cuando uses este modo, asegúrate de lo siguiente:
- Los valores de metadatos de tamaño mínimo y máximo se definen correctamente según el tamaño del contenido.
- El contenido es lo suficientemente flexible dentro del rango de tamaño esperado.
En general, debes usar este modo en los siguientes casos:
a) AppWidget
tiene un tamaño fijo.
b) No cambia su contenido cuando se le cambia el tamaño.
SizeMode.Responsive
Este modo es el equivalente a proporcionar diseños responsivos, que permite
el GlanceAppWidget
para definir un conjunto de diseños responsivos delimitados por
tamaños. Para cada tamaño definido, se crea el contenido y se asigna a la cuenta
cuando se crea o actualiza AppWidget
. A continuación, el sistema selecciona
más adecuada según el tamaño disponible.
Por ejemplo, en nuestro AppWidget
de destino, puedes definir tres tamaños y sus
contenido:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
En el ejemplo anterior, se llama al método provideContent
tres veces.
asignado al tamaño definido.
- En la primera llamada, el tamaño se evalúa como
100x100
. El contenido no incluir el botón extra, ni los textos de arriba o de abajo. - En la segunda llamada, el tamaño se evalúa como
250x100
. El contenido incluye botón adicional, pero no los textos de la parte superior y la inferior. - En la tercera llamada, el tamaño se evalúa como
250x250
. El contenido incluye el botón extra y ambos textos.
SizeMode.Responsive
es una combinación de los otros dos modos y te permite
a definir el contenido responsivo dentro de los límites predefinidos. En general, este modo
Funciona mejor y permite transiciones más fluidas cuando se cambia el tamaño de AppWidget
.
En la siguiente tabla, se muestra el valor del tamaño, según el SizeMode
y
el tamaño disponible de AppWidget
:
Tamaño disponible | 105 x 110 | 203 x 112 | 72 x 72 | 203 × 150 |
---|---|---|---|---|
SizeMode.Single |
110 x 110 | 110 x 110 | 110 x 110 | 110 x 110 |
SizeMode.Exact |
105 x 110 | 203 x 112 | 72 x 72 | 203 × 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* Los valores exactos son solo para fines de demostración. |
SizeMode.Exact
SizeMode.Exact
es el equivalente a proporcionar diseños exactos, que
solicita el contenido de GlanceAppWidget
cada vez que el tamaño disponible de AppWidget
cambia (por ejemplo, cuando el usuario cambia el tamaño de AppWidget
en la pantalla principal).
Por ejemplo, en el widget de destino, se puede agregar un botón adicional si el elemento el ancho disponible es mayor que un valor determinado.
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
Este modo proporciona más flexibilidad que los otros, pero incluye algunas advertencias:
- El elemento
AppWidget
debe volver a crearse por completo cada vez que cambie el tamaño. Esta puede provocar problemas de rendimiento y saltos en la IU cuando el contenido es complejo. - El tamaño disponible puede variar según la implementación del selector. Por ejemplo, si el selector no proporciona la lista de tamaños, el tamaño mínimo el tamaño posible.
- En dispositivos con versiones anteriores a Android 12, es posible que la lógica de cálculo del tamaño no funcione en todos los casos. situaciones.
En general, debes usar este modo si no se puede usar SizeMode.Responsive
.
(es decir, un pequeño conjunto de diseños adaptables no es factible).
Accede a recursos
Usa LocalContext.current
para acceder a cualquier recurso de Android, como se muestra en la
siguiente ejemplo:
LocalContext.current.getString(R.string.glance_title)
Te recomendamos que proporciones los IDs de recursos directamente para reducir el tamaño de la versión
RemoteViews
y para habilitar recursos dinámicos, como los objetos
de colores.
Los elementos componibles y los métodos aceptan recursos que usan un "proveedor", como
ImageProvider
o con un método de sobrecarga, como
GlanceModifier.background(R.color.blue)
Por ejemplo:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Cómo procesar texto
Glance 1.1.0 incluye una API para configurar tus estilos de texto. Establecer estilos de texto con
Atributos fontSize
, fontWeight
o fontFamily
de la clase TextStyle
fontFamily
admite todas las fuentes del sistema, como se muestra en el siguiente ejemplo, pero
No se admiten las fuentes personalizadas en las apps:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Cómo agregar botones compuestos
Los botones compuestos se introdujeron en Android 12. Vistazo hacia atrás compatibilidad para los siguientes tipos de botones compuestos:
Cada uno de estos botones compuestos muestra una vista en la que se puede hacer clic que representa la “marcado” para cada estado.
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
Cuando cambia el estado, se activa la expresión lambda proporcionada. Puedes almacenar el y verificar el estado, como se muestra en el siguiente ejemplo:
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
También puedes proporcionar el atributo colors
a CheckBox
, Switch
y
RadioButton
para personalizar sus colores:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )
Componentes adicionales
Glance 1.1.0 incluye el lanzamiento de componentes adicionales, como se describe en la siguiente tabla:
Name | Imagen | Vínculo de referencia | Notas adicionales |
---|---|---|---|
Botón relleno | Componente | ||
Botones con contorno | Componente | ||
Botones de íconos | Componente | Principal / secundario / Solo icono | |
Barra de título | Componente | ||
Scaffold | El andamiaje y la barra de título están en la misma demostración. |
Para obtener más información sobre los detalles del diseño, consulta los diseños de componentes en esta kit de diseño en Figma.