As seções a seguir descrevem como criar um widget de app básico com o Glance.
Declarar o AppWidget no manifesto
Depois de concluir as etapas de configuração, declare o AppWidget e os
metadados dele no app.
Estenda o receptor
AppWidgetdeGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
Registre o provedor do widget do app no arquivo
AndroidManifest.xmle no arquivo de metadados associado:<receiver android:name=".glance.MyReceiver" android:exported="true"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_app_widget_info" /> </receiver>
Adicionar os metadados AppWidgetProviderInfo
Em seguida, siga o guia Criar um widget para criar e definir as informações do widget
do app no arquivo @xml/my_app_widget_info.
A única diferença para o Resumo é que não há XML initialLayout, mas você
precisa definir um. É possível usar o layout de carregamento predefinido fornecido na
biblioteca:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>
Declarar o XML AppWidgetProviderInfo
O objeto AppWidgetProviderInfo define as qualidades essenciais do
widget. Defina o AppWidgetProviderInfo no arquivo de recursos de metadados XML
(res/xml/my_app_widget_info.xml) dentro de um elemento <appwidget-provider>:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:maxResizeWidth="250dp"
android:maxResizeHeight="120dp"
android:updatePeriodMillis="86400000"
android:description="@string/example_appwidget_description"
android:previewLayout="@layout/example_appwidget_preview"
android:initialLayout="@layout/glance_default_loading_layout"
android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>
Atributos de dimensionamento de widgets
A tela inicial padrão posiciona os widgets na janela com base em uma grade de células que têm altura e largura definidas. A maioria das telas iniciais só permite que os widgets tenham tamanhos que sejam múltiplos inteiros das células da grade, por exemplo, duas células na horizontal por três células na vertical.
Os atributos de dimensionamento do widget permitem especificar um tamanho padrão para o widget e fornecer limites inferiores e superiores para o tamanho dele. Nesse contexto, o tamanho padrão de um widget é o tamanho que ele assume quando é adicionado pela primeira vez à tela inicial.
A tabela a seguir descreve os atributos de <appwidget-provider> relacionados
ao dimensionamento de widgets:
| Atributos e descrição | |
|---|---|
targetCellWidth e
targetCellHeight (Android 12),
minWidth e minHeight |
targetCellWidth e
targetCellHeight, e minWidth e
minHeight, para que o app possa usar
minWidth e minHeight se o dispositivo do usuário
não for compatível com targetCellWidth e
targetCellHeight. Se houver suporte, os atributos
targetCellWidth e targetCellHeight
vão ter precedência sobre os atributos minWidth e minHeight.
|
minResizeWidth e
minResizeHeight |
Especifique o tamanho mínimo absoluto do widget. Esses valores especificam o
tamanho abaixo do qual o widget fica ilegível ou inutilizável. O uso
desses atributos permite que o usuário redimensione o widget para um tamanho menor
que o padrão. O atributo minResizeWidth será
ignorado se for maior que minWidth ou se o redimensionamento
horizontal não estiver ativado. Consulte
resizeMode. Da mesma forma, o
atributo minResizeHeight será ignorado se for maior que
minHeight ou se o redimensionamento vertical não estiver ativado. |
maxResizeWidth e
maxResizeHeight |
Especifique o tamanho máximo recomendado do widget. Se os valores não forem
um múltiplo das dimensões da célula da grade, eles serão arredondados para o tamanho de célula
mais próximo. O atributo maxResizeWidth será ignorado se for
menor que minWidth ou se o redimensionamento horizontal não estiver
ativado. Consulte resizeMode. Da mesma forma,
o atributo maxResizeHeight será ignorado se for menor
que minHeight ou se o redimensionamento vertical não estiver ativado.
Introduzido no Android 12. |
resizeMode |
Especifica as regras para redimensionar um widget. Você pode usar esse
atributo para tornar os widgets da tela inicial redimensionáveis horizontalmente, verticalmente
ou em ambos os eixos. Os usuários tocam e mantêm pressionado um widget para mostrar as alças de redimensionamento
e, em seguida, arrastam as alças horizontais ou verticais para mudar o tamanho dele no
layout de grade. Os valores do atributo resizeMode incluem
horizontal, vertical e none. Para
declarar um widget como redimensionável horizontal e verticalmente, use
horizontal|vertical. |
Exemplo
Para ilustrar como os atributos na tabela anterior afetam o dimensionamento do widget, considere as seguintes especificações:
- Uma célula da grade tem 30 dp de largura e 50 dp de altura.
- A seguinte especificação de atributo é fornecida:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="80dp"
android:minHeight="80dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:maxResizeWidth="120dp"
android:maxResizeHeight="120dp"
android:resizeMode="horizontal|vertical" />
A partir do Android 12:
Use os atributos targetCellWidth e targetCellHeight como o tamanho padrão
do widget.
O tamanho padrão do widget é 2x2. O widget pode ser redimensionado para 2x1 ou até 4x3.
Android 11 e versões anteriores:
Use os atributos minWidth e minHeight para calcular o tamanho padrão do
widget.
A largura padrão = Math.ceil(80 / 30) = 3
A altura padrão = Math.ceil(80 / 50) = 2
O tamanho padrão do widget é 3x2. O widget pode ser redimensionado para 2x1 ou até tela cheia.
Outros atributos de widget
A tabela a seguir descreve os atributos de <appwidget-provider> relacionados
a qualidades que não sejam o dimensionamento do widget.
| Atributos e descrição | |
|---|---|
updatePeriodMillis |
Define a frequência com que o framework do widget solicita uma atualização do
GlanceAppWidgetReceiver chamando o método de callback
onUpdate(). Recomendamos atualizar o mínimo possível,
no máximo uma vez por hora, para economizar bateria.
Para mais detalhes, consulte a seção Quando atualizar widgets no gerenciamento de estado do Resumo. |
initialLayout |
Aponta para o recurso de layout que define o layout de carregamento do widget antes que as composições da IU do Resumo sejam renderizadas. Você pode usar o layout de carregamento predefinido fornecido na biblioteca: @layout/glance_default_loading_layout. |
configure |
Define a atividade de configuração que é iniciada quando o usuário adiciona o widget. Consulte o guia Permitir que os usuários configurem widgets de apps. |
description |
Especifica a descrição que o seletor de widgets vai mostrar para seu widget. Introduzido no Android 12. |
previewLayout (Android 12)
e previewImage (Android 11 e versões anteriores) |
|
autoAdvanceViewId |
Especifica o ID da subvisualização do widget que é avançada automaticamente pelo host do widget. |
widgetCategory |
Declara se o widget pode ser mostrado na tela inicial
(home_screen), na tela de bloqueio (keyguard) ou em ambas. Para o Android 5.0 e versões mais recentes, apenas home_screen é válido. |
widgetFeatures |
Declara os recursos compatíveis com o widget. Por exemplo, se a configuração do widget for opcional, especifique configuration_optional e reconfigurable. |
Definir GlanceAppWidget
Crie uma classe que estenda
GlanceAppWidgete substitua o métodoprovideGlance. Esse é o método em que você pode carregar os dados necessários para renderizar o widget:class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // In this method, load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here Text("Hello World") } } }
Instancie-o no
glanceAppWidgetdo seuGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
Você configurou um AppWidget usando o Resumo.
Gerenciar a memória de imagens de widgets em buckets de tamanho
Quando o widget exibir bitmaps ou URIs de imagem content://, mantenha a memória total
serializada da imagem RemoteViews dentro do limite do sistema (6 * width *
height bytes, aplicados com um IllegalArgumentException no Android 17 e
versões mais recentes) fazendo o seguinte:
- Decodificar bitmaps compartilhados antes de
provideContent:se o widget usarSizeMode.ResponsiveouSizeMode.Exact, o Resumo vai executar a lambdaprovideContentuma vez para cada bucketDpSize. Decodificar umBitmapdentro deprovideContentaloca uma instância deBitmapseparada para cada bucket, multiplicando o uso da memória emRemoteViews.mBitmapCache. Decodifique bitmaps com redução de resolução (Bitmap.Config.RGB_565para imagens opacas) uma vez dentro deprovideGlanceantes deprovideContentpara que todos os buckets compartilhem uma única referência deBitmap. Use a importação
ImageProvidercorrespondente:o Jetpack Glance oferece duas sobrecargasImageProviderdistintas em dois pacotes:- Use
import androidx.glance.ImageProviderao transmitir umImageProvider(bitmap: Bitmap)na memória ou um recurso drawableImageProvider(resId: Int). - Use
import androidx.glance.appwidget.ImageProviderao transmitir um URIImageProvider(uri: Uri). Ao exibir arquivos em cache por um URIFileProvider, concedaIntent.FLAG_GRANT_READ_URI_PERMISSIONa todos os pacotes de inicializaçãoIntent.CATEGORY_HOMEinstalados para que o host possa renderizar a imagem.
- Use
Usar a classe GlanceAppWidgetReceiver para processar transmissões de widgets
O widget de coordenadas GlanceAppWidgetReceiver transmite e atualiza o estado da plataforma
estendendo o AppWidgetProvider. Ele recebe
eventos da plataforma quando o widget é atualizado, excluído, ativado ou desativado,
traduzindo-os em solicitações de ciclo de vida do Compose.
Declarar um widget no manifesto
Declare a subclasse da classe GlanceAppWidgetReceiver como um broadcast receiver no
arquivo AndroidManifest.xml:
<receiver android:name="MyReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/my_app_widget_info" />
</receiver>
O elemento <receiver> requer o atributo android:name, que especifica
a classe do receptor. O receptor precisa aceitar a ação de transmissão
ACTION_APPWIDGET_UPDATE dentro do <intent-filter>.
O elemento <meta-data> precisa identificar o nome como
android.appwidget.provider, e o atributo android:resource precisa apontar para
o recurso de metadados XML AppWidgetProviderInfo (@xml/my_app_widget_info).
Implementar a classe GlanceAppWidgetReceiver
No Resumo, você estende GlanceAppWidgetReceiver em vez de
AppWidgetProvider diretamente. Implemente-o vinculando o receptor à instância
GlanceAppWidget. Os principais callbacks disponíveis em
GlanceAppWidgetReceiver funcionam da seguinte maneira:
onUpdate(): substituído automaticamente pelo Resumo para executar atualizações de composição. Se você substituironUpdatemanualmente, precisará chamarsuper.onUpdatepara permitir que o Resumo inicie as linhas de execução de composição.onAppWidgetOptionsChanged(): chamado quando o widget é colocado ou redimensionado pela primeira vez. O Resumo lê os itens do pacote de opções em segundo plano para que o layout se ajuste perfeitamente com base nas dimensões do ambiente de execução.onDeleted(Context, IntArray): invocado sempre que uma instância de widget específica é excluída pelo usuário.onEnabled(Context): acionado quando a primeira instância do widget é criada. Excelente para executar migrações globais.onDisabled(Context): chamado quando a última instância ativa do provedor é removida.onReceive(Context, Intent): intercepta todas as transmissões da plataforma antes de métodos de callback específicos. É preciso garantir que qualquer lógica de receptor personalizada que você escreva chamesuper.onReceive(context, intent)e nunca chamegoAsync, já que o Resumo encaminha o trabalho automaticamente de forma assíncrona.
Receber intents de transmissão de widget
Nos bastidores, o GlanceAppWidgetReceiver filtra e processa as seguintes
intents de transmissão de widgets da plataforma:
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
Criar interface
O snippet a seguir demonstra como criar a interface:
/* Import Glance Composables In the event there is a name clash with the Compose classes of the same name, you may rename the imports per https://kotlinlang.org/docs/packages.html#imports using the `as` keyword. import androidx.glance.Button import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.text.Text */ class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { // Load data needed to render the AppWidget. // Use `withContext` to switch to another thread for long running // operations. provideContent { // create your AppWidget here MyContent() } } @Composable private fun MyContent() { Column( modifier = GlanceModifier.fillMaxSize(), verticalAlignment = Alignment.Top, horizontalAlignment = Alignment.CenterHorizontally ) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button( text = "Home", onClick = actionStartActivity<MyActivity>() ) Button( text = "Work", onClick = actionStartActivity<MyActivity>() ) } } } }
O exemplo de código anterior faz o seguinte:
- No
Columnde nível superior, os itens são colocados verticalmente um após o outro. - A
Columnexpande o tamanho para corresponder ao espaço disponível (com oGlanceModifier), alinha o conteúdo à parte de cima (verticalAlignment) e o centraliza horizontalmente (horizontalAlignment). - O conteúdo do
Columné definido usando o lambda. A ordem é importante.- O primeiro item na
Columné um componenteTextcom12.dpde padding. - O segundo item é um
Row, em que os itens são colocados horizontalmente, um após o outro, com doisButtonscentralizados horizontalmente (horizontalAlignment). A exibição final depende do espaço disponível. A imagem abaixo é um exemplo de como isso pode ser feito:
- O primeiro item na
É possível mudar os valores de alinhamento ou aplicar valores de modificador diferentes (como preenchimento) para mudar o posicionamento e o tamanho dos componentes. Consulte a documentação de referência para ver uma lista completa de componentes, parâmetros e modificadores disponíveis para cada classe.
Implementar cantos arredondados
O Android 12 introduz parâmetros do sistema para personalizar dinamicamente os raios dos cantos dos widgets do app:
system_app_widget_background_radius: especifica o raio do canto do contêiner de plano de fundo do widget (nunca maior que 28 dp).- Raio interno:para evitar o corte de conteúdo, calcule um raio proporcional
para o conteúdo interno com base no contorno do plano de fundo do sistema:
systemRadiusValue - widgetPadding
No Resumo, é possível aplicar propriedades de dimensionamento de raio de canto dinamicamente na
composição usando
GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).
Para compatibilidade com versões anteriores em dispositivos com o Android 11 (nível 30 da API) ou versões anteriores, implemente atributos personalizados e substitutos de recursos de tema personalizados:
/values/attrs.xml<resources> <attr name="backgroundRadius" format="dimension" /> </resources>/values/styles.xml<resources> <style name="MyWidgetTheme"> <item name="backgroundRadius">@dimen/my_background_radius_dimen</item> </style> </resources>/values-31/styles.xml<resources> <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight"> <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item> </style> </resources>/drawable/my_widget_background.xml<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <corners android:radius="?attr/backgroundRadius" /> </shape>