Esta página descreve como lidar com tamanhos e fornecer recursos flexíveis e layouts com o Glance, usando componentes atuais do Glance.
Usar Box
, Column
e Row
O Glance tem três layouts principais combináveis:
Box
: posiciona elementos sobre outros. Ele é traduzido como umRelativeLayout
.Column
: posiciona os elementos um após o outro no eixo vertical. Ela traduz para umaLinearLayout
com orientação vertical.Row
: posiciona os elementos um após o outro no eixo horizontal. Ela traduz para umaLinearLayout
com orientação horizontal.
O Glance oferece suporte a objetos Scaffold
. Coloque seus Column
, Row
e
Elementos combináveis Box
em um determinado objeto Scaffold
.
Cada um desses elementos combináveis permite definir os alinhamentos vertical e horizontal. do conteúdo e das restrições de largura, altura, peso ou padding usando modificadores. Além disso, cada filho pode definir seu próprio modificador para mudar o espaço e posicionamento dentro do principal.
O exemplo a seguir mostra como criar um Row
que distribui de forma uniforme
os filhos horizontalmente, como visto na Figura 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
A Row
preenche a largura máxima disponível e, como cada filho tem a mesma
peso, eles compartilham igualmente o espaço disponível. É possível definir pesos diferentes,
tamanhos, paddings ou alinhamentos para adaptar os layouts às suas necessidades.
Usar layouts roláveis
Outra maneira de fornecer conteúdo responsivo é torná-lo rolável. Isso é
possível com o elemento combinável LazyColumn
. Esse elemento combinável permite definir um conjunto
de itens a serem mostrados dentro de um contêiner rolável no widget do app.
Os snippets a seguir mostram maneiras diferentes de definir itens dentro da
LazyColumn
:
Você pode fornecer o número de itens:
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
Forneça itens individuais:
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
Forneça uma lista ou matriz de itens:
LazyColumn { items(peopleNameList) { name -> Text(name) } }
Também é possível usar uma combinação dos exemplos 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") } }
O snippet anterior não especifica o itemId
. Especificar o
O itemId
ajuda a melhorar o desempenho e manter a rolagem.
posição em listas e atualizações de appWidget
do Android 12 em diante (por
exemplo, ao adicionar ou remover itens da lista). O exemplo a seguir
mostra como especificar um itemId
:
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
Definir o SizeMode
Os tamanhos de AppWidget
podem variar dependendo do dispositivo, da escolha do usuário ou da tela de início.
por isso, é importante fornecer layouts flexíveis, como descrito em Fornecer
com layouts de widget flexíveis. O Glance simplifica isso com SizeMode
e o valor LocalSize
. As seções a seguir descrevem os três
dois modos.
SizeMode.Single
SizeMode.Single
é o modo padrão. Isso indica que apenas um tipo
conteúdo for fornecido; ou seja, mesmo que o tamanho disponível de AppWidget
mude,
o tamanho do conteúdo não é alterado.
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 // ... } }
Ao usar esse modo, verifique se:
- Os valores de metadados de tamanho mínimo e máximo são definidos adequadamente com base no tamanho do conteúdo.
- O conteúdo é flexível o suficiente dentro do intervalo de tamanho esperado.
Em geral, você deve usar esse modo quando:
a) o AppWidget
tiver um tamanho fixo;
b) o conteúdo não seja alterado quando redimensionado.
SizeMode.Responsive
Esse modo é o equivalente a fornecer layouts responsivos, o que permite
a GlanceAppWidget
para definir um conjunto de layouts responsivos limitados por medidas
tamanhos. Para cada tamanho definido, o conteúdo é criado e mapeado para o
tamanho quando a AppWidget
é criada ou atualizada. Em seguida, o sistema seleciona
o melhor ajuste, aquele com base no tamanho disponível.
Por exemplo, no AppWidget
de destino, é possível definir três tamanhos e
conteúdo:
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") } } } }
No exemplo anterior, o método provideContent
é chamado três vezes e
mapeado para o tamanho definido.
- Na primeira chamada, o tamanho é avaliado como
100x100
. O conteúdo não incluem o botão extra nem os textos das partes de cima e de baixo. - Na segunda chamada, o tamanho é avaliado como
250x100
. O conteúdo inclui botão extra, mas não os textos de cima e de baixo. - Na terceira chamada, o tamanho é avaliado como
250x250
. O conteúdo inclui botão extra e ambos os textos.
O SizeMode.Responsive
é uma combinação dos outros dois modos e permite
definir o conteúdo responsivo dentro de limites predefinidos. Em geral, esse modo
tem melhor desempenho e permite transições mais suaves quando a AppWidget
é redimensionada.
A tabela a seguir mostra o valor do tamanho, dependendo do SizeMode
e
o tamanho disponível de AppWidget
:
Tamanho disponível | 105 x 110 | 203 x 112 | 72 x 72 | 203 x 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 x 150 |
SizeMode.Responsive |
80 x 100 | 80 x 100 | 80 x 100 | 150 x 120 |
* Os valores exatos são apenas para fins de demonstração. |
SizeMode.Exact
SizeMode.Exact
é o equivalente a fornecer layouts exatos, que
solicita o conteúdo GlanceAppWidget
sempre que o tamanho AppWidget
disponível
muda (por exemplo, quando o usuário redimensiona o AppWidget
na tela inicial).
Por exemplo, no widget de destino, um botão extra pode ser adicionado se o largura disponível é maior do que um determinado valor.
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") } } } } }
Esse modo oferece mais flexibilidade que os outros, mas vem com alguns ressalvas:
- O
AppWidget
precisa ser totalmente recriado toda vez que o tamanho mudar. Isso pode levar a problemas de desempenho e saltos na interface quando o conteúdo é complexo. - O tamanho disponível pode ser diferente dependendo da implementação do inicializador. Por exemplo, se a tela de início não fornecer a lista de tamanhos, o mínimo tamanho possível é usado.
- Em dispositivos anteriores ao Android 12, a lógica de cálculo de tamanho pode não funcionar em todos em diferentes situações.
Em geral, use esse modo se não for possível usar SizeMode.Responsive
.
(ou seja, não é viável ter um pequeno conjunto de layouts responsivos).
Acessar recursos
Use LocalContext.current
para acessar qualquer recurso do Android, conforme mostrado no
exemplo a seguir:
LocalContext.current.getString(R.string.glance_title)
Recomendamos fornecer IDs de recursos diretamente para reduzir o tamanho do arquivo final
RemoteViews
e para ativar recursos dinâmicos, como dynamic
cores.
Os elementos combináveis e métodos aceitam recursos usando um "provedor", como
ImageProvider
ou usando um método de sobrecarga como
GlanceModifier.background(R.color.blue)
. Exemplo:
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
Processar texto
O Glance 1.1.0 inclui uma API para definir estilos de texto. Definir estilos de texto usando
Atributos fontSize
, fontWeight
ou fontFamily
da classe TextStyle.
fontFamily
oferece suporte a todas as fontes do sistema, como mostrado no exemplo abaixo, mas
fontes personalizadas em apps não são compatíveis:
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
Adicionar botões compostos
Os botões compostos foram lançados no Android 12. O Glance oferece suporte para versões anteriores compatibilidade para os seguintes tipos de botões compostos:
Cada botão composto exibe uma visualização clicável que representa "marcado" 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" )
Quando o estado muda, o lambda fornecido é acionado. É possível armazenar verificar o estado, como mostrado no exemplo a seguir:
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) } ) } }
Também é possível fornecer o atributo colors
para CheckBox
, Switch
e
RadioButton
para personalizar as cores:
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) ), )
Outros componentes
O Glance 1.1.0 inclui o lançamento de outros componentes, conforme descrito no tabela a seguir:
Nome | Imagem | Link de referência | Outras observações |
---|---|---|---|
Botão preenchido | Componente | ||
Botões de contorno | Componente | ||
Botões de ícone | Componente | Principal / Secundário / Apenas ícones | |
Barra de título | Componente | ||
Scaffold | Scaffold e barra de título estão na mesma demonstração. |
Para mais informações sobre detalhes de design, consulte os designs de componentes neste kit de design no Figma.