Создайте простой виджет

Попробуйте способ Compose
Jetpack Compose — рекомендуемый набор инструментов пользовательского интерфейса для Android. Узнайте, как создавать виджеты с использованием API в стиле Compose.

Виджеты приложений — это миниатюрные представления приложений, которые можно встраивать в другие приложения, например, на домашний экран, и получать периодические обновления. Эти представления называются виджетами в пользовательском интерфейсе, и вы можете опубликовать один из них с помощью поставщика виджетов приложений (или поставщика виджетов ). Компонент приложения, который содержит другие виджеты, называется хостом виджетов приложений (или хостом виджетов ). На рисунке 1 показан пример музыкального виджета:

Пример музыкального виджета
Рисунок 1. Пример музыкального виджета.

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

Информацию о том, как создать свой виджет, см. в разделе Обзор виджетов приложений .

Компоненты виджета

Для создания виджета вам понадобятся следующие основные компоненты:

Объект AppWidgetProviderInfo
Описывает метаданные для виджета, такие как макет виджета, частота обновления и класс AppWidgetProvider . AppWidgetProviderInfo определяется в XML , как описано в этом документе.
Класс AppWidgetProvider
Определяет основные методы, которые позволяют вам программно взаимодействовать с виджетом. Через него вы получаете трансляции, когда виджет обновляется, включается, отключается или удаляется. Вы объявляете AppWidgetProvider в манифесте , а затем реализуете его, как описано в этом документе.
Посмотреть макет
Определяет начальный макет для виджета. Макет определяется в XML , как описано в этом документе.

На рисунке 2 показано, как эти компоненты вписываются в общий процесс обработки виджета приложения.

Процесс обработки виджета приложения
Рисунок 2. Процесс обработки виджета приложения.

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

Мы также рекомендуем следующие улучшения: гибкие макеты виджетов , различные улучшения , расширенные виджеты , виджеты-коллекции и создание хоста виджетов .

Объявите XML-файл AppWidgetProviderInfo

Объект AppWidgetProviderInfo определяет основные качества виджета. Определите объект AppWidgetProviderInfo в файле ресурсов XML с помощью одного элемента <appwidget-provider> и сохраните его в папке res/xml/ проекта.

Это показано в следующем примере:

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

Атрибуты размера виджета

Домашний экран по умолчанию размещает виджеты в своем окне на основе сетки ячеек, имеющих определенную высоту и ширину. Большинство домашних экранов позволяют виджетам принимать только размеры, кратные ячейкам сетки, например, две ячейки по горизонтали на три ячейки по вертикали.

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

В следующей таблице описаны атрибуты <appwidget-provider> , относящиеся к размеру виджета:

Атрибуты и описание
targetCellWidth и targetCellHeight (Android 12), minWidth и minHeight
  • Начиная с Android 12, атрибуты targetCellWidth и targetCellHeight определяют размер виджета по умолчанию в ячейках сетки. Эти атрибуты игнорируются в Android 11 и ниже и могут игнорироваться, если домашний экран не поддерживает макет на основе сетки.
  • Атрибуты minWidth и minHeight определяют размер виджета по умолчанию в dp. Если значения минимальной ширины или высоты виджета не соответствуют размерам ячеек, то значения округляются до ближайшего размера ячейки.
Мы рекомендуем указывать оба набора атрибутов — targetCellWidth и targetCellHeight , а также minWidth и minHeight — чтобы ваше приложение могло вернуться к использованию minWidth и minHeight , если устройство пользователя не поддерживает targetCellWidth и targetCellHeight . Если поддерживаются, атрибуты targetCellWidth и targetCellHeight имеют приоритет над атрибутами minWidth и minHeight .
minResizeWidth и minResizeHeight Укажите абсолютный минимальный размер виджета. Эти значения определяют размер, ниже которого виджет становится нечитаемым или иным образом непригодным для использования. Использование этих атрибутов позволяет пользователю изменять размер виджета до размера, который меньше размера виджета по умолчанию. Атрибут minResizeWidth игнорируется, если он больше minWidth или если горизонтальное изменение размера не включено. См. resizeMode . Аналогично, атрибут minResizeHeight игнорируется, если он больше minHeight или если вертикальное изменение размера не включено.
maxResizeWidth и maxResizeHeight Укажите рекомендуемый максимальный размер виджета. Если значения не кратны размерам ячеек сетки, они округляются до ближайшего размера ячейки. Атрибут maxResizeWidth игнорируется, если он меньше minWidth или если горизонтальное изменение размера не включено. См. resizeMode . Аналогично, атрибут maxResizeHeight игнорируется, если он больше minHeight или если вертикальное изменение размера не включено. Представлено в Android 12.
resizeMode Задает правила, по которым можно изменять размер виджета. С помощью этого атрибута можно изменять размер виджетов домашнего экрана по горизонтали, вертикали или по обеим осям. Пользователи касаются и удерживают виджет, чтобы отобразить его маркеры изменения размера, затем перетаскивают горизонтальные или вертикальные маркеры, чтобы изменить его размер на сетке макета. Значения атрибута resizeMode включают в себя horizontal , vertical и none . Чтобы объявить виджет изменяемым по горизонтали и вертикали, используйте horizontal|vertical .

Пример

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

  • Ячейка сетки имеет ширину 30 dp и высоту 50 dp.
  • Предоставляется следующая спецификация атрибутов:
<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" />

Начиная с Android 12:

Используйте атрибуты targetCellWidth и targetCellHeight в качестве размера виджета по умолчанию.

Размер виджета по умолчанию 2x2. Виджет можно уменьшить до 2x1 или увеличить до 4x3.

Android 11 и ниже:

Используйте атрибуты minWidth и minHeight для вычисления размера виджета по умолчанию.

Ширина по умолчанию = Math.ceil(80 / 30) = 3

Высота по умолчанию = Math.ceil(80 / 50) = 2

Размер виджета по умолчанию 3x2. Виджет можно уменьшить до 2x1 или увеличить до полного экрана.

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

В следующей таблице описаны атрибуты <appwidget-provider> , относящиеся к качествам, отличным от размера виджета.

Атрибуты и описание
updatePeriodMillis Определяет, как часто фреймворк виджета запрашивает обновление у AppWidgetProvider , вызывая метод обратного вызова onUpdate() . Фактическое обновление не гарантируется точно вовремя с этим значением, и мы рекомендуем обновляться как можно реже — не чаще одного раза в час — для экономии заряда батареи. Полный список соображений для выбора подходящего периода обновления см. в разделе Оптимизации для обновления содержимого виджета .
initialLayout Указывает на ресурс макета, который определяет макет виджета.
configure Определяет активность, которая запускается, когда пользователь добавляет виджет, позволяя ему настраивать свойства виджета. См. Разрешить пользователям настраивать виджеты . Начиная с Android 12, ваше приложение может пропустить начальную настройку. См. Использование конфигурации виджета по умолчанию для получения подробной информации.
description Указывает описание для выбора виджета, которое будет отображаться для вашего виджета. Представлено в Android 12.
previewLayout (Android 12) и previewImage (Android 11 и ниже)
  • Начиная с Android 12, атрибут previewLayout определяет масштабируемый предварительный просмотр, который вы предоставляете как XML-макет, установленный на размер виджета по умолчанию. В идеале XML-макет, указанный как этот атрибут, является тем же XML-макетом, что и фактический виджет с реалистичными значениями по умолчанию.
  • В Android 11 или ниже атрибут previewImage указывает предварительный просмотр того, как будет выглядеть виджет после настройки, который пользователь видит при выборе виджета приложения. Если этот атрибут не указан, пользователь вместо этого видит значок запуска вашего приложения. Это поле соответствует атрибуту android:previewImage в элементе <receiver> в файле AndroidManifest.xml .
Примечание: Мы рекомендуем указывать атрибуты previewImage и previewLayout , чтобы ваше приложение могло вернуться к использованию previewImage , если устройство пользователя не поддерживает previewLayout . Для получения более подробной информации см. Обратная совместимость с масштабируемыми предпросмотрами виджетов .
autoAdvanceViewId Указывает идентификатор представления подпредставления виджета, которое автоматически перемещается хостом виджета.
widgetCategory Указывает, может ли ваш виджет отображаться на главном экране ( home_screen ), экране блокировки ( keyguard ) или на обоих. Для Android 5.0 и выше действителен только home_screen .
widgetFeatures Объявляет функции, поддерживаемые виджетом. Например, если вы хотите, чтобы ваш виджет использовал свою конфигурацию по умолчанию, когда пользователь добавляет его, укажите оба флага configuration_optional и reconfigurable . Это позволяет обойти запуск действия по настройке после того, как пользователь добавляет виджет. Пользователь все равно может перенастроить виджет впоследствии.

Используйте класс AppWidgetProvider для обработки трансляций виджетов

Класс AppWidgetProvider обрабатывает трансляции виджетов и обновляет виджет в ответ на события жизненного цикла виджета. В следующих разделах описывается, как объявить AppWidgetProvider в манифесте и затем реализовать его.

Объявите виджет в манифесте

Сначала объявите класс AppWidgetProvider в файле AndroidManifest.xml вашего приложения, как показано в следующем примере:

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

Элемент <receiver> требует атрибута android:name , который указывает AppWidgetProvider , используемый виджетом. Компонент не должен экспортироваться, если только отдельный процесс не должен транслировать ваш AppWidgetProvider , что обычно не так.

Элемент <intent-filter> должен включать элемент <action> с атрибутом android:name . Этот атрибут указывает, что AppWidgetProvider принимает трансляцию ACTION_APPWIDGET_UPDATE . Это единственная трансляция, которую необходимо явно объявить. AppWidgetManager автоматически отправляет все остальные трансляции виджетов в AppWidgetProvider по мере необходимости.

Элемент <meta-data> определяет ресурс AppWidgetProviderInfo и требует следующих атрибутов:

  • android:name : указывает имя метаданных. Используйте android.appwidget.provider для идентификации данных как дескриптора AppWidgetProviderInfo .
  • android:resource : указывает расположение ресурса AppWidgetProviderInfo .

Реализовать класс AppWidgetProvider

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

onUpdate()
Это вызывается для обновления виджета с интервалами, определенными атрибутом updatePeriodMillis в AppWidgetProviderInfo . Для получения дополнительной информации см. таблицу с описанием дополнительных атрибутов виджета на этой странице.
Этот метод также вызывается, когда пользователь добавляет виджет, поэтому он выполняет необходимую настройку, например, определяет обработчики событий для объектов View или запускает задания по загрузке данных для отображения в виджете. Однако, если вы объявляете действие конфигурации без флага configuration_optional , этот метод не вызывается, когда пользователь добавляет виджет, но вызывается для последующих обновлений. Действие конфигурации отвечает за выполнение первого обновления после завершения настройки. Дополнительные сведения см. в разделе Разрешить пользователям настраивать виджеты приложений .
Самый важный обратный вызов — onUpdate() . Для получения дополнительной информации см. Обработка событий с помощью класса onUpdate() на этой странице.
onAppWidgetOptionsChanged()

Это вызывается при первом размещении виджета и каждый раз при изменении размера виджета. Используйте этот обратный вызов, чтобы показать или скрыть содержимое на основе диапазонов размеров виджета. Получите диапазоны размеров — и, начиная с Android 12, список возможных размеров, которые может принять экземпляр виджета — вызвав getAppWidgetOptions() , который возвращает Bundle , включающий следующее:

  • OPTION_APPWIDGET_MIN_WIDTH : содержит нижнюю границу ширины экземпляра виджета в единицах dp.
  • OPTION_APPWIDGET_MIN_HEIGHT : содержит нижнюю границу высоты экземпляра виджета в единицах dp.
  • OPTION_APPWIDGET_MAX_WIDTH : содержит верхнюю границу ширины экземпляра виджета в единицах dp.
  • OPTION_APPWIDGET_MAX_HEIGHT : содержит верхнюю границу высоты экземпляра виджета в единицах dp.
  • OPTION_APPWIDGET_SIZES : содержит список возможных размеров ( List<SizeF> ) в единицах dp, которые может принимать экземпляр виджета. Представлено в Android 12.
onDeleted(Context, int[])

Это вызывается каждый раз, когда виджет удаляется с хоста виджета.

onEnabled(Context)

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

onDisabled(Context)

Это вызывается, когда последний экземпляр вашего виджета удаляется из хоста виджета. Здесь вы очищаете любую работу, выполненную в onEnabled(Context) , например, удаление временной базы данных.

onReceive(Context, Intent)

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

Вы должны объявить реализацию класса AppWidgetProvider как широковещательный приемник, используя элемент <receiver> в AndroidManifest . Для получения дополнительной информации см. раздел Объявление виджета в манифесте на этой странице.

Обработка событий с помощью класса onUpdate()

Самый важный обратный вызов AppWidgetProvideronUpdate() , поскольку он вызывается при добавлении каждого виджета на хост, если только вы не используете конфигурационную активность без флага configuration_optional . Если ваш виджет принимает какие-либо события взаимодействия с пользователем, зарегистрируйте обработчики событий в этом обратном вызове. Если ваш виджет не создает временные файлы или базы данных или не выполняет другую работу, требующую очистки, то onUpdate() может быть единственным методом обратного вызова, который вам нужно определить.

Например, если вам нужен виджет с кнопкой, которая запускает действие при нажатии, вы можете использовать следующую реализацию AppWidgetProvider :

Котлин

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

Ява

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);
        }
    }
}

Этот AppWidgetProvider определяет только метод onUpdate() , используя его для создания PendingIntent , который запускает Activity и прикрепляет его к кнопке виджета с помощью setOnClickPendingIntent(int, PendingIntent) . Он включает цикл, который перебирает каждую запись в appWidgetIds , который является массивом идентификаторов, идентифицирующих каждый виджет, созданный этим поставщиком. Если пользователь создает более одного экземпляра виджета, то все они обновляются одновременно. Однако для всех экземпляров виджета управляется только одно расписание updatePeriodMillis . Например, если расписание обновления определено каждые два часа, а второй экземпляр виджета добавляется через час после первого, то они оба обновляются в период, определенный первым, а второй период обновления игнорируется. Они оба обновляются каждые два часа, а не каждый час.

Более подробную информацию см. в примере класса ExampleAppWidgetProvider.java .

Получать виджеты трансляции намерений

AppWidgetProvider — это класс удобства. Если вы хотите получать трансляции виджета напрямую, вы можете реализовать собственный BroadcastReceiver или переопределить обратный вызов onReceive(Context,Intent) . Вам нужно позаботиться о следующих намерениях:

Создайте макет виджета

Вы должны определить начальный макет для вашего виджета в XML и сохранить его в каталоге res/layout/ проекта. Подробности см. в руководстве по дизайну .

Создание макета виджета не составит труда, если вы знакомы с макетами . Однако имейте в виду, что макеты виджетов основаны на RemoteViews , который не поддерживает все виды макетов или виджетов представлений. Вы не можете использовать пользовательские представления или подклассы представлений, поддерживаемых RemoteViews .

RemoteViews также поддерживает ViewStub — невидимый View нулевого размера, который можно использовать для ленивого увеличения ресурсов макета во время выполнения.

Поддержка поведения с отслеживанием состояния

В Android 12 добавлена ​​поддержка поведения с отслеживанием состояния с использованием следующих существующих компонентов:

Виджет по-прежнему не имеет состояния. Ваше приложение должно хранить состояние и регистрироваться для событий изменения состояния.

Пример виджета списка покупок, демонстрирующего поведение с отслеживанием состояния
Рисунок 3. Пример поведения с отслеживанием состояния.

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

Котлин

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

Ява

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

Предоставьте два макета: один для устройств под управлением Android 12 или более поздней версии в res/layout-v31 , а другой для устройств под управлением Android 11 или более ранних версий в папке res/layout по умолчанию.

Реализовать закругленные углы

В Android 12 представлены следующие системные параметры для установки радиусов скругления углов вашего виджета:

  • system_app_widget_background_radius : радиус угла фона виджета, который никогда не превышает 28 dp.

  • Внутренний радиус, который можно рассчитать из внешнего радиуса и отступа. Смотрите следующий фрагмент:

    /**
     * Applies corner radius for views that are visually positioned [widgetPadding]dp inside of the
     * widget background.
     */
    @Composable
    fun GlanceModifier.appWidgetInnerCornerRadius(widgetPadding: Dp): GlanceModifier {
    
        if (Build.VERSION.SDK_INT < 31) {
            return this
        }
    
        val resources = LocalContext.current.resources
        // get dimension in float (without rounding).
        val px = resources.getDimension(android.R.dimen.system_app_widget_background_radius)
        val widgetBackgroundRadiusDpValue = px / resources.displayMetrics.density
        if (widgetBackgroundRadiusDpValue < widgetPadding.value) {
            return this
        }
        return this.cornerRadius(Dp(widgetBackgroundRadiusDpValue - widgetPadding.value))
    }

Чтобы рассчитать подходящий радиус для внутреннего содержимого вашего виджета, используйте следующую формулу: systemRadiusValue - widgetPadding

Виджеты, обрезающие свое содержимое до непрямоугольных фигур, должны использовать @android:id/background в качестве идентификатора фонового представления, для которого параметр android:clipToOutline установлен в true .

Важные соображения относительно закругленных углов

  • Сторонние лаунчеры и производители устройств могут переопределить параметр system_app_widget_background_radius , сделав его меньше 28 dp.
  • Если ваш виджет не использует @android:id/background или не определяет фон, который обрезает его содержимое на основе контура (при android:clipToOutline установленном в true , то средство запуска автоматически определяет фон и обрезает виджет, используя прямоугольник со скругленными углами, установленными в соответствии с системным радиусом.

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

  • Начиная с Android 16 системное значение AOSP для system_app_widget_background_radius равно 24dp . Лаунчеры и производители устройств могут обрезать виджет до system_app_widget_background_radius .

  • Внутреннее содержимое виджета должно иметь достаточный отступ для поддержки значений радиуса system_app_widget_background_radius до 28dp , чтобы избежать обрезки содержимого закругленными углами.

Для совместимости виджетов с предыдущими версиями Android мы рекомендуем определить пользовательские атрибуты и использовать пользовательскую тему для их переопределения для Android 12, как показано в следующих примерах XML-файлов:

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