Os widgets de apps são visualizações em miniatura que podem ser incorporadas a outros apps, como a tela inicial, e receber atualizações periódicas. Essas visualizações são chamadas de widgets na interface do usuário, e você pode publicar uma com um provedor de widgets de apps (ou provedor de widgets). Um componente do app que contém outros widgets é chamado de host de widget de app (ou host de widget). A Figura 1 mostra um exemplo de widget de música:
Neste documento, descrevemos como publicar um widget usando um provedor. Para
saber mais sobre como criar seu próprio AppWidgetHost
para
hospedar widgets de apps, consulte Criar um host de widgets.
Para saber mais sobre como projetar seu widget, consulte Visão geral dos widgets de apps.
Componentes do widget
Para criar um widget, você precisa dos seguintes componentes básicos:
- Objeto
AppWidgetProviderInfo
- Descreve os metadados de um widget, como o layout, a frequência
de atualização e a classe
AppWidgetProvider
.AppWidgetProviderInfo
é definido em XML, conforme descrito neste documento. - Classe
AppWidgetProvider
- Define os métodos básicos que permitem interagir programaticamente com o
widget. Por meio dele, você recebe transmissões quando o widget é atualizado,
ativado, desativado ou excluído. Declarar
AppWidgetProvider
no manifesto e, em seguida, implementá-lo, conforme descrito neste documento. - Layout de visualização
- Define o layout inicial do widget. O layout é definido em XML, conforme descrito neste documento.
A Figura 2 mostra como esses componentes se encaixam no fluxo de processamento geral do widget de app.
Caso o widget precise de configuração do usuário, implemente a atividade de configuração do widget de app. Essa atividade permite que os usuários modifiquem as configurações do widget, como o fuso horário de um widget de relógio.
- A partir do Android 12 (nível 31 da API), é possível fornecer uma configuração padrão e permitir que os usuários reconfigurem o widget mais tarde. Para mais detalhes, consulte Usar a configuração padrão do widget e Permitir que os usuários reconfigurem widgets colocados.
- No Android 11 (nível 30 da API) ou versões anteriores, essa atividade é iniciada sempre que o usuário adiciona o widget à tela inicial.
Também recomendamos as seguintes melhorias: layouts flexíveis de widgets, melhorias diversas, widgets avançados, widgets de coleção e criação de um host de widget.
Declarar o XML do AppWidgetProviderInfo
O objeto AppWidgetProviderInfo
define as qualidades essenciais de um widget.
Defina o objeto AppWidgetProviderInfo
em um arquivo de recurso XML usando um único
elemento <appwidget-provider>
e salve-o na pasta res/xml/
do projeto.
Isso é mostrado neste exemplo:
<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/example_loading_appwidget"
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 widgets na janela com base em uma grade de células com altura e largura definidas. A maioria das telas iniciais só permite que os widgets assumam tamanhos que sejam múltiplos inteiros das células da grade. Por exemplo, duas células horizontalmente por três células na vertical.
Os atributos de dimensionamento permitem especificar um tamanho padrão e fornecer limites inferior e superior para o tamanho do widget. Nesse contexto, o tamanho padrão de um widget é o que ele assume quando é adicionado pela primeira vez à tela inicial.
A tabela abaixo descreve os atributos <appwidget-provider>
relacionados
ao dimensionamento do widget:
Atributos e descrição | |
---|---|
targetCellWidth e
targetCellHeight (Android 12),
minWidth e minHeight |
targetCellWidth e
targetCellHeight , e minWidth e
minHeight ) para que o app possa voltar a usar
minWidth e minHeight caso o dispositivo do usuário
não seja compatível com targetCellWidth e
targetCellHeight . Se compatíveis, os atributos targetCellWidth e targetCellHeight terão precedência sobre os atributos minWidth e minHeight .
|
minResizeWidth e
minResizeHeight |
Especifique o tamanho mínimo absoluto do widget. Esses valores especificam o
tamanho em que 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 de 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 maior
que minHeight ou se o redimensionamento vertical não estiver ativado.
Introduzido no Android 12. |
resizeMode |
Especifica as regras pelas quais um widget pode ser redimensionado. 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 em um widget e o mantêm pressionado para mostrar as alças de redimensionamento e, em seguida, arrastam as alças horizontais ou verticais para mudar o tamanho na grade de layout. 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, suponha as seguintes especificações:
- Uma célula de 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 do widget é 2x2 por padrão. 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 do widget é 3x2 por padrão. O widget pode ser redimensionado para 2x1 ou até tela cheia.
Outros atributos do widget
A tabela a seguir descreve os atributos <appwidget-provider>
relacionados
a qualidades diferentes do dimensionamento do widget.
Atributos e descrição | |
---|---|
updatePeriodMillis |
Define com que frequência o framework do widget solicita uma atualização do
AppWidgetProvider chamando o método de callback
onUpdate() . Não há garantia de que a atualização real ocorra exatamente
no horário com esse valor, e recomendamos que ela seja atualizada com a menor
frequência possível (não mais do que uma vez por hora) para economizar a bateria.
Para ver a lista completa de considerações para escolher um período de atualização apropriado, consulte Otimizações para atualizar o conteúdo do widget. |
initialLayout |
Aponta para o recurso de layout que define o layout do widget. |
configure |
Define a atividade iniciada quando o usuário adiciona o widget, permitindo que ele configure as propriedades do widget. Consulte Permitir que os usuários configurem widgets. A partir do Android 12, o app pode pular a configuração inicial. Consulte Usar a configuração padrão do widget para ver mais detalhes. |
description |
Especifica a descrição a ser exibida pelo seletor para seu widget. Introduzido no Android 12. |
previewLayout (Android 12)
e previewImage (Android 11 e versões anteriores) |
previewImage
e previewLayout para que seu app possa voltar
a usar previewImage se o dispositivo do usuário não for compatível com
previewLayout . Para mais detalhes, consulte
Compatibilidade com versões anteriores com visualizações de
widgets escalonáveis.
|
autoAdvanceViewId |
Especifica o ID da visualização da subvisualização do widget que é avançada automaticamente pelo host do widget. |
widgetCategory |
Declara se o widget pode ser exibido 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 com suporte do widget. Por exemplo, se você quiser que o widget use a configuração padrão quando for adicionado, especifique as sinalizações configuration_optional e reconfigurable . Isso ignora a inicialização da atividade de configuração depois que um usuário
adiciona o widget. O usuário ainda pode
reconfigurar o widget
depois. |
Usar a classe AppWidgetProvider para processar transmissões de widgets
A classe AppWidgetProvider
processa as transmissões do widget e o atualiza
em resposta aos eventos de ciclo de vida. As seções abaixo descrevem como
declarar AppWidgetProvider
no manifesto e implementá-lo.
Declarar um widget no manifesto
Primeiro, declare a classe AppWidgetProvider
no arquivo AndroidManifest.xml
do seu app, conforme mostrado no exemplo a seguir:
<receiver android:name="ExampleAppWidgetProvider"
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/example_appwidget_info" />
</receiver>
O elemento <receiver>
requer o atributo android:name
, que especifica
o AppWidgetProvider
usado pelo widget. O componente não pode ser exportado,
a menos que um processo separado precise transmitir para o AppWidgetProvider
, o que
geralmente não é o caso.
O elemento <intent-filter>
precisa incluir um elemento <action>
com o
atributo android:name
. Esse atributo especifica que a AppWidgetProvider
aceita a
transmissão
ACTION_APPWIDGET_UPDATE
. Essa é a única transmissão que precisa ser declarada explicitamente. O
AppWidgetManager
envia automaticamente todas as outras transmissões de widget para o AppWidgetProvider
, conforme
necessário.
O elemento <meta-data>
especifica o recurso AppWidgetProviderInfo
e
exige estes atributos:
android:name
: especifica o nome dos metadados. Useandroid.appwidget.provider
para identificar os dados como o descritorAppWidgetProviderInfo
.android:resource
: especifica o local do recursoAppWidgetProviderInfo
.
Implementar a classe AppWidgetProvider
A classe AppWidgetProvider
estende
BroadcastReceiver
como uma
classe de conveniência para processar transmissões de widgets. Ele recebe apenas as transmissões
de eventos relevantes para o widget, como quando o widget é atualizado,
excluído, ativado e desativado. Quando esses eventos de transmissão ocorrem, os métodos
AppWidgetProvider
abaixo são chamados:
onUpdate()
- É chamado para atualizar o widget em intervalos definidos pelo
atributo
updatePeriodMillis
naAppWidgetProviderInfo
. Consulte a tabela que descreve outros atributos de widget nesta página para mais informações. - Esse método também é chamado quando o usuário adiciona o widget. Ele executa a
configuração essencial, como definir manipuladores de eventos para objetos
View
ou iniciar jobs para carregar dados a serem exibidos no widget. No entanto, se você declarar uma atividade de configuração sem a flagconfiguration_optional
, esse método não será chamado quando o usuário adicionar o widget, mas será chamado para as atualizações subsequentes. É responsabilidade da atividade de configuração realizar a primeira atualização quando a configuração for concluída. Consulte Permitir que os usuários configurem widgets de apps para mais informações. - O callback mais importante é
onUpdate()
. Para mais informações, consulte Processar eventos com a classeonUpdate()
nesta página. onAppWidgetOptionsChanged()
Ele é chamado quando o widget é colocado pela primeira vez e sempre que ele é redimensionado. Use esse callback para mostrar ou ocultar conteúdo com base nos intervalos de tamanho do widget. Confira os intervalos de tamanho e, a partir do Android 12, a lista de tamanhos possíveis que uma instância de widget pode ter, chamando
getAppWidgetOptions()
, que retorna umaBundle
que inclui o seguinte:OPTION_APPWIDGET_MIN_WIDTH
: contém o limite inferior da largura, em unidades dp, de uma instância de widget.OPTION_APPWIDGET_MIN_HEIGHT
: contém o limite inferior da altura, em unidades dp, de uma instância de widget.OPTION_APPWIDGET_MAX_WIDTH
: contém o limite superior da largura, em unidades dp, de uma instância de widget.OPTION_APPWIDGET_MAX_HEIGHT
: contém o limite superior da altura, em unidades dp, de uma instância de widget.OPTION_APPWIDGET_SIZES
: contém a lista de tamanhos possíveis (List<SizeF>
), em unidades de dp, que uma instância de widget pode usar. Introduzido no Android 12.
onDeleted(Context, int[])
É chamado sempre que um widget é excluído do host.
onEnabled(Context)
Ele é chamado quando uma instância do widget é criada pela primeira vez. Por exemplo, se o usuário adicionar duas instâncias do widget, isso será chamado apenas na primeira vez. Se você precisar abrir um novo banco de dados ou realizar outra configuração que só precisa ocorrer uma vez para todas as instâncias de widget, este é um bom lugar para fazer isso.
onDisabled(Context)
Isso é chamado quando a última instância do widget é excluída do host do widget. É aqui que você limpa qualquer trabalho feito em
onEnabled(Context)
, como a exclusão de um banco de dados temporário.onReceive(Context, Intent)
Ele é chamado para cada transmissão e antes de cada um dos métodos de callback anteriores. Normalmente, não é necessário implementar esse método, porque a implementação
AppWidgetProvider
padrão filtra todas as transmissões do widget e chama os métodos anteriores, conforme apropriado.
É necessário declarar a implementação da classe AppWidgetProvider
como um broadcast
receiver usando o elemento <receiver>
no AndroidManifest
. Consulte Declarar um
widget no manifesto nesta página para mais informações.
Processar eventos com a classe onUpdate()
O callback AppWidgetProvider
mais importante é onUpdate()
, porque ele é
chamado quando cada widget é adicionado a um host, a menos que você use uma atividade
de configuração sem a flag configuration_optional
. Se o widget aceitar qualquer evento de interação do usuário, registre os manipuladores de eventos nesse callback. Se
o widget não criar arquivos ou bancos de dados temporários nem executar outro trabalho
que exija limpeza, onUpdate()
poderá ser o único método de callback que você
precisa definir.
Por exemplo, se você quiser um widget com um botão que inicie uma atividade quando
tocado, poderá usar a seguinte implementação de AppWidgetProvider
:
Kotlin
class ExampleAppWidgetProvider : AppWidgetProvider() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Perform this loop procedure for each widget that belongs to this // provider. appWidgetIds.forEach { appWidgetId -> // Create an Intent to launch ExampleActivity. val pendingIntent: PendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ Intent(context, ExampleActivity::class.java), /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE ) // Get the layout for the widget and attach an onClick listener to // the button. val views: RemoteViews = RemoteViews( context.packageName, R.layout.appwidget_provider_layout ).apply { setOnClickPendingIntent(R.id.button, pendingIntent) } // Tell the AppWidgetManager to perform an update on the current // widget. appWidgetManager.updateAppWidget(appWidgetId, views) } } }
Java
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // Perform this loop procedure for each widget that belongs to this // provider. for (int i=0; i < appWidgetIds.length; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity( /* context = */ context, /* requestCode = */ 0, /* intent = */ intent, /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); // Get the layout for the widget and attach an onClick listener to // the button. RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app // widget. appWidgetManager.updateAppWidget(appWidgetId, views); } } }
Esse AppWidgetProvider
define apenas o método onUpdate()
, usando-o para
criar uma PendingIntent
que inicia
uma Activity
e a anexa ao botão
do widget usando setOnClickPendingIntent(int,
PendingIntent)
. Ele inclui uma repetição que itera em cada entrada
em appWidgetIds
, que é uma matriz de IDs que identificam cada widget criado por
esse provedor. Se o usuário criar mais de uma instância do widget, todas elas serão atualizadas simultaneamente. No entanto, apenas uma programação updatePeriodMillis
é gerenciada para todas as instâncias do widget. Por exemplo, se a programação de atualização
for definida para ocorrer a cada duas horas e uma segunda instância do widget for adicionada
uma hora após a primeira, ambas serão atualizadas no período definido pela
primeira, e o segundo período de atualização será ignorado. Ambos são atualizados a cada duas
horas, não a cada hora.
Consulte a
classe de exemplo
ExampleAppWidgetProvider.java
para saber mais.
Receber intents de transmissão de widget
AppWidgetProvider
é uma classe de conveniência. Se você quiser receber as transmissões
de widget diretamente, implemente seu próprio BroadcastReceiver
ou substitua
o callback
onReceive(Context,Intent)
. Estas são as intents que precisam ser consideradas:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
Criar o layout do widget
Defina um layout inicial para o widget em XML e salve-o no
diretório res/layout/
do projeto. Consulte as Diretrizes de
design para detalhes.
A criação do layout de widget é simples se você tiver familiaridade com
layouts. No entanto, lembre-se de que os layouts
de widget são baseados em RemoteViews
,
que não oferece suporte a todos os tipos de widget de visualização ou layout. Não é possível usar visualizações
personalizadas ou subclasses das visualizações com suporte de RemoteViews
.
A RemoteViews
também oferece suporte a ViewStub
,
que é um View
invisível e de tamanho zero que pode ser usado para inflar lentamente os recursos de layout
durante a execução.
Suporte para comportamento com estado
O Android 12 adiciona suporte a comportamentos com estado usando estes componentes já existentes:
O widget ainda não tem estado. O app precisa armazenar o estado e se registrar para eventos de mudança de estado.
O exemplo de código a seguir mostra como implementar esses componentes.
Kotlin
// Check the view. remoteView.setCompoundButtonChecked(R.id.my_checkbox, true) // Check a radio group. remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2) // Listen for check changes. The intent has an extra with the key // EXTRA_CHECKED that specifies the current checked state of the view. remoteView.setOnCheckedChangeResponse( R.id.my_checkbox, RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent) )
Java
// Check the view. remoteView.setCompoundButtonChecked(R.id.my_checkbox, true); // Check a radio group. remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2); // Listen for check changes. The intent has an extra with the key // EXTRA_CHECKED that specifies the current checked state of the view. remoteView.setOnCheckedChangeResponse( R.id.my_checkbox, RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));
Forneça dois layouts: um destinado a dispositivos com o Android 12 ou
versões mais recentes em res/layout-v31
e o outro direcionado ao Android 11 anterior
na pasta res/layout
padrão.
Implementar cantos arredondados
O Android 12 apresenta os parâmetros do sistema abaixo para definir o raio dos cantos arredondados do widget:
system_app_widget_background_radius
: o raio do canto do plano de fundo do widget, que nunca é maior que 28 dp.system_app_widget_inner_radius
: o raio do canto de qualquer visualização dentro do widget. Esse valor é exatamente 8 dp a menos que o raio do plano de fundo para se alinhar bem ao usar um padding de 8 dp.
O exemplo a seguir mostra um widget que usa
system_app_widget_background_radius
para o canto e
system_app_widget_inner_radius
para as visualizações dentro do widget.
1 Canto do widget.
2 Canto de uma visualização dentro do widget.
Considerações importantes sobre cantos arredondados
- As telas de início e os fabricantes de dispositivos de terceiros podem substituir o parâmetro
system_app_widget_background_radius
para que ele seja menor que 28 dp. O parâmetrosystem_app_widget_inner_radius
é sempre 8 dp menor que o valor desystem_app_widget_background_radius
. - Se o widget não usar
@android:id/background
ou definir um plano de fundo que recorte o conteúdo com base no contorno (comandroid:clipToOutline
definido comotrue
), a tela de início vai identificar o plano de fundo automaticamente e recortar o widget usando um retângulo com cantos arredondados de até 16 dp. Consulte Verificar se o widget é compatível com o Android 12.
Para compatibilidade de widgets com versões anteriores do Android, recomendamos definir atributos personalizados e usar um tema personalizado para substituí-los no Android 12, conforme mostrado nos exemplos de arquivos XML abaixo:
/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>
/layout/my_widget_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
...
android:background="@drawable/my_widget_background" />