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

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

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

Подробнее о том, как добавить в приложение виджеты и обновления в реальном времени…

Как добавить сгенерированные изображения для предпросмотра

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

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

Система не предоставляет обратный вызов для предпросмотра, поэтому ваше приложение должно самостоятельно определять, когда вызывать метод 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, заданного для размера виджета по умолчанию. Раньше в качестве предпросмотра виджета использовался статический ресурс drawable, из-за чего в некоторых случаях предпросмотр неточно отражал то, как виджет будет выглядеть после добавления на главный экран.

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

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

Рекомендуем использовать тот же макет, что и в виджете, с реалистичными значениями по умолчанию или тестовыми значениями. В большинстве приложений используются одни и те же значения previewLayout и initialLayout. Инструкции по созданию точных макетов для предпросмотра приведены в статье Как создавать точные макеты для предпросмотра с динамическими элементами.

Мы рекомендуем указывать оба атрибута: previewLayout и previewImage. В этом случае, если устройство пользователя не поддерживает previewLayout, приложение сможет использовать previewImage. Атрибут "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>