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

Попробуйте способ «Композиции»
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()

Самый важный обратный вызов AppWidgetProvider — это onUpdate() , поскольку он вызывается при добавлении каждого виджета на хост, если только вы не используете конфигурационное действие без флага 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" />