Criar um widget avançado

Esta página explica as práticas recomendadas para criar um widget mais avançado para uma melhor experiência do usuário.

Otimizações para atualizar o conteúdo do widget

Atualizar o conteúdo do widget pode ser caro em termos computacionais. Para economizar bateria e otimizar o tipo, a frequência e o tempo da atualização.

Tipos de atualização de widgets

Há três maneiras de atualizar um widget: uma atualização completa, uma atualização parcial e, no caso de um widget de coleta, uma atualização de dados. Cada um tem uma custos computacionais e ramificações.

A seguir, descrevemos cada tipo de atualização e fornecemos snippets de código para cada um.

  • Atualização completa:chame AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews). para atualizar completamente o widget. Isso substitui o modelo RemoteViews com um novo RemoteViews. Essa é a atualização mais cara em termos computacionais.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout1, "Updated text1")
    setTextViewText(R.id.textview_widget_layout2, "Updated text2")
    }
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1");
    remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2");
    appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
    
  • Atualização parcial:chamada AppWidgetManager.partiallyUpdateAppWidget para atualizar partes do widget. Isso mescla o novo RemoteViews com o fornecido anteriormente: RemoteViews. Esse método será ignorado se um widget não receber pelo menos uma atualização completa pelo updateAppWidget(int[], RemoteViews).

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also {
    setTextViewText(R.id.textview_widget_layout, "Updated text")
    }
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
    remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text");
    appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
    
  • Atualização de dados de coleta: chamada AppWidgetManager.notifyAppWidgetViewDataChanged para invalidar os dados de uma visualização de coleção no widget. Isso aciona RemoteViewsFactory.onDataSetChanged Enquanto isso, os dados antigos serão exibidos no widget. Você pode realizar tarefas caras de forma síncrona com esse método.

    Kotlin

    val appWidgetManager = AppWidgetManager.getInstance(context)
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
    

    Java

    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
    appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
    

É possível chamar esses métodos de qualquer lugar no app, desde que o app tenha o mesmo UID que o classe AppWidgetProvider.

Determinar a frequência de atualização de um widget

Os widgets são atualizados periodicamente, dependendo do valor fornecido para o updatePeriodMillis . O widget pode ser atualizado em resposta à interação do usuário, atualizações ou ambos.

Atualizar periodicamente

Você pode controlar a frequência da atualização periódica especificando um valor para AppWidgetProviderInfo.updatePeriodMillis no XML appwidget-provider. Cada atualização aciona o método AppWidgetProvider.onUpdate(), que é onde você inserir o código para atualizar o widget. No entanto, considere as alternativas para atualizações de broadcast receiver descritas em seção a seguir se o widget precisar carregar dados de forma assíncrona ou demorar mais de 10 segundos para atualizar, porque depois de 10 segundos, o sistema considera uma BroadcastReceiver não responda.

updatePeriodMillis não oferece suporte a valores menores que 30 minutos. No entanto, se Se quiser desativar as atualizações periódicas, especifique 0.

É possível permitir que os usuários ajustem a frequência das atualizações em uma configuração. Para Por exemplo, ele pode querer que o mostrador da bolsa seja atualizado a cada 15 minutos ou somente a vezes por dia. Nesse caso, defina updatePeriodMillis como 0 e use WorkManager.

Atualizar em resposta a uma interação do usuário

Aqui estão algumas maneiras recomendadas de atualizar o widget com base na interação do usuário:

  • A partir de uma atividade do app:chame diretamente AppWidgetManager.updateAppWidget em resposta a uma interação do usuário, como com o toque do usuário.

  • Em interações remotas, como uma notificação ou um widget de app: construa uma PendingIntent e atualize o widget a partir da Activity, Broadcast ou Service. Você pode escolher sua própria prioridade. Para Por exemplo, se você selecionar um Broadcast para a PendingIntent, poderá escolher uma transmissão em primeiro plano para dar Prioridade BroadcastReceiver.

Atualizar em resposta a um evento de transmissão

Um exemplo de evento de transmissão que requer a atualização de um widget é quando a o usuário tira uma foto. Neste caso, convém atualizar o widget quando uma nova foto é detectado.

É possível programar um job com JobScheduler e especificar uma transmissão como a usando o JobInfo.Builder.addTriggerContentUri .

Você também pode registrar um BroadcastReceiver para a transmissão, por exemplo: ouvindo ACTION_LOCALE_CHANGED. No entanto, como isso consome recursos do dispositivo, use com cuidado e ouça apenas para a transmissão específica. Com a introdução do recurso de transmissão limitações no Android 7.0 (nível 24 da API) e Android 8.0 (nível 26 da API), os apps não podem registrar apps implícitos em seus manifestos, com determinadas exceções.

Considerações ao atualizar um widget usando um BroadcastReceiver

Se o widget for atualizado com base em um BroadcastReceiver, incluindo AppWidgetProvider, lembre-se das seguintes considerações sobre o duração e prioridade de uma atualização de widget.

Duração da atualização

Como regra geral, o sistema permite que os broadcast receivers, que normalmente são executados no linha de execução principal, execute por até 10 segundos antes de considerar que não são responsivas e para acionar um aplicativo não Erro de resposta (ANR). Se demorar mais para atualize o widget, considere as seguintes alternativas:

  • Programe uma tarefa usando o WorkManager.

  • Dê mais tempo ao destinatário usando a método goAsync. Isso permite que os receptores sejam executados por 30 segundos.

Consulte Considerações sobre segurança e práticas para saber mais informações imprecisas ou inadequadas.

Prioridade da atualização

Por padrão, as transmissões, incluindo as feitas com o AppWidgetProvider.onUpdate: são executados como processos em segundo plano. Isso significa que recursos do sistema sobrecarregados podem causar um atraso na invocação da transmissão receptor. Para priorizar a transmissão, torne-a um processo em primeiro plano.

Por exemplo, adicione o Intent.FLAG_RECEIVER_FOREGROUND flag para o Intent transmitida para PendingIntent.getBroadcast quando o usuário toca em determinada parte do widget.

Crie visualizações precisas que incluam itens dinâmicos

Figura 1 . Visualização de widget sem itens de lista.

Esta seção explica a abordagem recomendada para exibir vários itens em uma visualização para um widget com uma coleção visualização, ou seja, um widget que usa uma ListView, GridView ou StackView.

Caso seu widget use uma dessas visualizações, criar uma visualização escalonável diretamente fornecendo o widget real layout prejudica a quando a visualização do widget não exibe itens. Isso ocorre porque dados de visualização de coleção são definidos dinamicamente no tempo de execução e são semelhantes à como mostrado na figura 1.

Para que as visualizações de widgets com visualizações de coleção sejam exibidas corretamente no widget seletor, recomendamos manter um arquivo de layout separado, designado apenas para o prévia. Esse arquivo separado inclui o layout real do widget e um exibição de marcadores de posição da coleção com itens falsos. Por exemplo, é possível imitar ListView fornecendo um marcador de posição LinearLayout com várias listas fictícias itens.

Para ilustrar um exemplo de ListView, comece com um arquivo de layout separado:

// res/layout/widget_preview.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:layout_height="wrap_content"
   android:background="@drawable/widget_background"
   android:orientation="vertical">

    // Include the actual widget layout that contains ListView.
    <include
        layout="@layout/widget_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    // The number of fake items you include depends on the values you provide
    // for minHeight or targetCellHeight in the AppWidgetProviderInfo
    // definition.

    <TextView android:text="@string/fake_item1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

    <TextView android:text="@string/fake_item2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginVertical="?attr/appWidgetInternalPadding" />

</LinearLayout>

Especifique o arquivo de layout da visualização ao fornecer o atributo previewLayout do os metadados AppWidgetProviderInfo. Você ainda especifica o layout real do widget. para o atributo initialLayout e use o layout do widget real ao criar um RemoteViews no momento da execução.

<appwidget-provider
    previewLayout="@layout/widget_previe"
    initialLayout="@layout/widget_view" />

Itens de lista complexos

O exemplo na seção anterior apresenta itens de lista falsos, porque a lista itens são objetos TextView. Pode ser mais complexo para fornecer itens falsos se os itens forem layouts complexos.

Considere um item de lista definido em widget_list_item.xml e que consiste em dois objetos TextView:

<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

    <TextView android:id="@id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_title" />

    <TextView android:id="@id/content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/fake_content" />
</LinearLayout>

Para fornecer itens de lista falsos, é possível incluir o layout várias vezes, mas isso faz com que cada item da lista seja idêntico. Para fornecer itens de lista exclusivos, siga estas etapas:

  1. Crie um conjunto de atributos para os valores de texto:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Use estes atributos para definir o texto:

    <LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">
    
        <TextView android:id="@id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetTitle" />
    
        <TextView android:id="@id/content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="?widgetContent" />
    </LinearLayout>
    
  3. Crie quantos estilos forem necessários para a visualização. Redefina os valores em cada estilo:

    <resources>
    
        <style name="Theme.Widget.ListItem">
            <item name="widgetTitle"></item>
            <item name="widgetContent"></item>
        </style>
        <style name="Theme.Widget.ListItem.Preview1">
            <item name="widgetTitle">Fake Title 1</item>
            <item name="widgetContent">Fake content 1</item>
        </style>
        <style name="Theme.Widget.ListItem.Preview2">
            <item name="widgetTitle">Fake title 2</item>
            <item name="widgetContent">Fake content 2</item>
        </style>
    
    </resources>
    
  4. Aplique os estilos aos itens falsos no layout de visualização:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:layout_width="match_parent"
       android:layout_height="wrap_content" ...>
    
        <include layout="@layout/widget_view" ... />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview1" />
    
        <include layout="@layout/widget_list_item"
            android:theme="@style/Theme.Widget.ListItem.Preview2" />
    
    </LinearLayout>