Как добавить предпросмотр в меню выбора виджетов

Чтобы улучшить работу с виджетами в приложении, предоставьте сгенерированный предпросмотр виджета на устройствах с Android 15 и более поздними версиями, масштабированный предпросмотр виджета (указав previewLayout) для устройств с Android 12–14 и previewImage для более ранних версий.

Сгенерированные предпросмотры позволяют создавать динамические персонализированные предпросмотры виджетов, которые точно отражают, как они будут выглядеть на главном экране пользователя. В Android 15 и более поздних версиях они предоставляются через Push API. Это значит, что приложение может предоставить предпросмотр в любой момент своего жизненного цикла, не получая явного запроса от хоста виджета.

Дополнительную информацию можно найти в видео Добавьте в приложение виджеты и обновления в реальном времени на YouTube.

Добавить сгенерированные изображения

Чтобы показывать сгенерированные предпросмотры виджетов на устройствах с Android 15 или более поздней версией ОС, сначала задайте для compileSdk значение 35 или выше в файле модуля build.gradle. Это позволит передавать RemoteViews в средство выбора виджетов.

Приложения могут использовать setWidgetPreview в AppWidgetManager. Чтобы предотвратить злоупотребления и снизить нагрузку на систему, в setWidgetPreview ограничена частота запросов. По умолчанию лимит составляет примерно два вызова в час.

Система не предоставляет обратный вызов для предварительного просмотра, поэтому ваше приложение должно самостоятельно решать, когда вызывать setWidgetPreviews. Стратегия обновления зависит от варианта использования вашего виджета:

  • Если виджет содержит статическую информацию или выполняет быстрое действие, настройте предварительный просмотр при первом запуске приложения.
  • Вы можете настроить предварительный просмотр, как только в приложении появятся данные, например после входа пользователя или первоначальной настройки.
  • Вы можете настроить периодическую задачу, чтобы обновлять предварительные версии с выбранной частотой.

В следующем примере загружается ресурс макета виджета XML и устанавливается в качестве предварительного просмотра. Чтобы метод setWidgetPreview отображался в этом фрагменте кода, в настройках сборки compileSdk должно быть указано значение 35 или выше.

AppWidgetManager.getInstance(appContext).setWidgetPreview(
    ComponentName(
        appContext,
        ExampleAppWidgetReceiver::class.java
    ),
    AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
    RemoteViews("com.example", R.layout.widget_preview)
)

Добавьте масштабируемые предварительные версии виджетов

Начиная с Android 12, предварительный просмотр виджета, отображаемый в средстве выбора виджетов, можно масштабировать. Вы предоставляете его в виде макета XML, заданного для размера виджета по умолчанию. Раньше предпросмотр виджета был статическим прорисовываемым ресурсом, из-за чего в некоторых случаях виджеты в предпросмотре выглядели не так, как на главном экране.

Чтобы реализовать масштабируемый предпросмотр виджета, используйте атрибут previewLayout элемента appwidget-provider, чтобы указать макет XML:

<appwidget-provider
    android:previewLayout="@layout/my_widget_preview">
</appwidget-provider>

Рекомендуем использовать тот же макет, что и у фактического виджета, с реалистичными значениями по умолчанию или тестовыми значениями. В большинстве приложений используются одни и те же значения previewLayout и initialLayout. Подробнее о том, как создавать точные макеты для предварительного просмотра, включающие динамические элементы…

Рекомендуем указывать оба атрибута (previewLayout и previewImage), чтобы приложение могло использовать previewImage, если устройство пользователя не поддерживает previewLayout. Атрибут previewLayout имеет приоритет над атрибутом previewImage.

Добавьте статические изображения для предварительного просмотра виджетов, чтобы обеспечить обратную совместимость

Чтобы виджеты в меню выбора на устройствах с Android 11 (уровень API 30) или более ранней версии показывали предпросмотр или чтобы у вас был резервный вариант для масштабируемого предпросмотра, укажите атрибут previewImage.

Если вы измените внешний вид виджета, обновите изображение для предварительного просмотра.

Этот атрибут также используется в качестве резервного для сгенерированных предварительных просмотров, если вы не установили его с помощью setWidgetPreview.

Создавайте точные предварительные версии, включающие динамические элементы.

Рисунок 1. Предварительный просмотр виджета, в котором не отображаются элементы списка.

В этом разделе описывается рекомендуемый подход к отображению нескольких элементов в предварительном просмотре виджета с представлением коллекции, то есть виджета, в котором используется ListView, GridView или StackView. Это относится к масштабируемым предпросмотрам виджетов, а не к сгенерированным.

Если ваш виджет использует одно из этих представлений, создание масштабируемого предварительного просмотра путем предоставления фактического макета виджета непосредственно в previewLayout может ухудшить работу, когда в предварительном просмотре виджета не отображаются элементы. Это происходит потому, что данные представления коллекции задаются динамически во время выполнения и выглядят аналогично изображению, показанному на рисунке 1.

Чтобы предварительный просмотр виджетов с представлениями коллекций правильно отображался в средстве выбора виджетов, мы рекомендуем поддерживать отдельный файл макета, предназначенный только для предварительного просмотра. В этот файл макета необходимо включить следующее:

  • Макет виджета.
  • Плейсхолдер коллекции с поддельными элементами. Например, вы можете имитировать ListView, предоставив плейсхолдер LinearLayout с несколькими поддельными элементами списка.

Чтобы проиллюстрировать пример для ListView, начните с отдельного файла макета:

// 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>

Укажите файл макета предварительного просмотра в атрибуте previewLayout метаданных AppWidgetProviderInfo. Вы по-прежнему указываете фактический макет виджета для атрибута initialLayout и используете фактический макет виджета при создании RemoteViews во время выполнения.

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

Сложные элементы списка

В примере из предыдущего раздела используются поддельные элементы списка, поскольку они являются объектами TextView. Если макеты элементов сложные, то и создавать поддельные элементы может быть сложнее.

Рассмотрим элемент списка, который определен в widget_list_item.xml и состоит из двух объектов 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>

Чтобы предоставить поддельные элементы списка, вы можете включить макет несколько раз, но это приведет к тому, что каждый элемент списка будет идентичным. Чтобы указать уникальные элементы списка, выполните следующие действия:

  1. Создайте набор атрибутов для текстовых значений:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Для настройки текста используйте следующие атрибуты:

    <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. Создайте столько стилей, сколько нужно для предварительного просмотра. Переопределите значения в каждом стиле:

    <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. Примените стили к фиктивным элементам в макете предварительного просмотра:

    <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>