Android 12 위젯 개선사항

Android 12에서는 기존 Widgets API를 개선하여 플랫폼과 런처에서 사용자 및 개발자 환경을 개선합니다. 이 가이드를 통해 위젯이 Android 12와 호환되는지 확인하는 방법을 알아보고 기존 위젯을 새로고침하는 API에 관한 참조로도 이 가이드를 사용하세요.

대체 텍스트

위젯이 Android 12와 호환되는지 확인

Android 12의 위젯은 둥근 모서리를 사용합니다. 앱 위젯이 Android 12 이상을 실행하는 기기에서 사용되면 런처가 위젯의 배경을 자동으로 식별하여 둥근 모서리가 되도록 자릅니다.

이 시나리오에서는 위젯이 다음 조건 중 하나에서 올바르게 표시되지 않을 수 있습니다.

  • 위젯의 모서리에 콘텐츠가 포함됨: 모서리 부분에 있는 일부 콘텐츠가 잘릴 수 있습니다.

  • 위젯이 잘릴 수 없는 배경을 사용합니다. 여기에는 투명 배경이나 빈 뷰 또는 레이아웃, 자르기 어려운 기타 모든 종류의 특수 배경이 포함됩니다. 시스템은 사용할 배경을 올바르게 식별하지 못할 수 있습니다.

위젯이 이 변경사항으로 영향을 받는다면 둥근 모서리로 새로고침하여(다음 섹션에서 설명) 올바르게 표시되도록 하는 것이 좋습니다.

샘플 사용

이러한 모든 새 API의 실제 사례를 확인하려면 샘플 목록 위젯을 참고하세요.

둥근 모서리 구현

Android 12에서는 다음 시스템 매개변수를 도입하여 위젯의 둥근 모서리 반경을 설정합니다.

다음 예는 위젯 모서리에 system_app_widget_background_radius를 사용하고 위젯 내부 뷰에 system_app_widget_inner_radius를 사용하는 위젯을 보여줍니다.

대체 텍스트

1 위젯의 모서리

2 위젯 내부의 뷰 모서리

둥근 모서리와의 하위 호환성

이전 버전의 Android와 위젯 호환성을 보장하려면 다음 XML 파일 예와 같이 맞춤 속성을 정의하고 맞춤 테마를 사용하여 Android 12용으로 재정의하는 것이 좋습니다.

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

동적 색상 적용

Android 12에서 위젯은 버튼과 배경, 기타 구성요소에 기기 테마 색상을 사용할 수 있습니다. 이를 통해 여러 위젯 간에 전환이 원활해지고 일관성이 유지될 수 있습니다.

다음 예에서는 기기 테마 색상이 '갈색'이므로 강조 색상과 위젯 배경이 조정됩니다. 시스템의 기본 테마(@android:style/Theme.DeviceDefault.DayNight)와 색상 속성을 사용하여 이를 달성할 수 있습니다. 흔히 사용되는 색상 속성은 다음과 같습니다.

  • ?android:attr/colorAccent
  • ?android:attr/colorBackground
  • ?android:attr/textColorPrimary?android:attr/textColorSecondary
밝은 모드 테마 위젯
밝은 테마 위젯
어두운 모드 테마 위젯
어두운 테마 위젯

동적 색상과의 하위 호환성

맞춤 테마를 만들고 Android 12용으로 재정의하는 것이 좋습니다. 다음 예는 다양한 유형의 XML 파일로 이를 실행하는 방법을 보여줍니다.

/values/styles.xml

<resources>
  <style name="MyWidget.TextView">
    <item name="android:textColor">@color/my_text_color</item>
  </style>
  <style name="MyWidgetTheme">
    <item name="textViewStyle">@style/MyWidget.TextView</item>
  </style>
</resources>

/values-31/styles.xml

<resources>
  <style name="MyWidgetTheme" parent="Theme.DeviceDefault.DayNight" />
</resources>

/layout/my_widget_layout.xml

<resources>
  <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:theme="@style/MyWidgetTheme" />
</resources>

더 쉽게 위젯 맞춤설정

appwidget-providerconfigure 속성으로 구성 활동을 지정하면 앱 위젯 호스트는 사용자가 위젯을 홈 화면에 추가한 직후 구성 활동을 실행합니다.

Android 12에서는 사용자에게 더 나은 구성 환경을 제공할 수 있는 새 옵션을 추가합니다.

사용자가 배치된 위젯을 재구성할 수 있도록 함

재구성 가능으로 라벨이 지정된 위젯을 구성하려면 사용자가 위젯을 길게 누르면 됩니다. 그러면 재구성 버튼이 표시되며 이 버튼을 탭하여 설정을 변경할 수 있습니다.

대체 텍스트

1 재구성 버튼

appwidget-providerwidgetFeatures 속성에 reconfigurable 플래그를 지정합니다.

<appwidget-provider
  ...
  android:configure="com.myapp.WidgetConfigActivity"
  android:widgetFeatures="reconfigurable">
</appwidget-provider>

위젯의 기본 구성 사용

사용자가 위젯을 추가할 때 위젯이 기본 구성을 사용하도록 하려면 configuration_optionalreconfigurable 플래그를 모두 widgetFeatures 필드에 지정하여 구성 단계를 건너뛰면 됩니다. 이를 통해 사용자가 위젯을 추가한 후 구성 활동 실행을 우회할 수 있습니다. 앞서 언급했듯이 사용자는 나중에 위젯을 재구성할 수 있습니다.

예를 들어 시계 위젯은 초기 구성을 우회하고 기본적으로 기기 시간대를 표시할 수 있습니다.

<appwidget-provider
  ...
  android:configure="com.myapp.WidgetConfigActivity"
  android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

위젯 구성 옵션과의 하위 호환성

앱은 이전 버전의 Android에서 configuration_optionalreconfigurable 플래그를 사용할 수 있습니다. 그러나 이러한 플래그는 아무런 영향을 미치지 않고 시스템은 여전히 구성 활동을 실행합니다.

새 복합 버튼 추가

Android 12에서는 다음 기존 구성요소를 사용하여 스테이트풀(Stateful) 동작 지원을 새로 추가합니다.

위젯은 여전히 스테이트리스(Stateless)입니다. 앱은 상태를 저장하고 상태 변경 이벤트에 등록해야 합니다.

대체 텍스트

다음 코드 예는 이러한 구성요소를 구현하는 방법을 보여줍니다.

Kotlin

// 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 will have 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 will have 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를 타겟팅하는 레이아웃(기본 res/layout 폴더)입니다.

위젯 크기 및 레이아웃에 개선된 API 사용

Android 12부터 다음을 실행하여 더 정밀한 크기 속성과 유연한 레이아웃을 제공할 수 있습니다.

  1. 추가 위젯 크기 제약 조건 지정

  2. 반응형 레이아웃이나 정확한 레이아웃 제공

추가 위젯 크기 제약 조건 지정

Android 12에서는 화면 크기가 다양한 여러 기기에서 위젯의 크기를 더 안정적으로 조절할 수 있도록 하는 새 API를 추가합니다.

기존 minWidth, minHeight, minResizeWidth, minResizeHeight 속성 외에 다음 새 appwidget-provider 속성을 사용합니다.

  • targetCellWidthtargetCellHeight: 런처 그리드 셀 측면에서 위젯의 타겟 크기를 정의합니다. 이러한 속성을 정의하면 minWidthminHeight 대신 사용됩니다.

  • maxResizeWidthmaxResizeHeight: 런처에서 사용자가 위젯의 크기를 조절할 수 있는 최대 크기를 정의합니다.

다음 XML은 크기 조절 속성을 사용하는 방법을 설명합니다.

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

반응형 레이아웃 제공

위젯 크기에 따라 레이아웃을 변경해야 하면 각각 크기 범위에 유효한 작은 레이아웃 세트를 만드는 것이 좋습니다. 만들 수 없다면 런타임 시 정확한 위젯 크기에 기반하여 레이아웃을 제공하는 방법도 있습니다.

이 기능을 구현하면 더 원활하게 크기가 조절되고 전반적인 시스템 상태가 개선됩니다. 시스템이 다른 크기로 위젯을 표시할 때마다 앱의 절전 모드를 해제하지 않아도 되기 때문입니다.

다음 코드 예는 레이아웃 목록을 제공하는 방법을 보여줍니다.

Kotlin

override fun onUpdate(...) {
  val smallView = ...
  val tallView = ...
  val wideView = ...

  val viewMapping: Map<SizeF, RemoteViews> = mapOf(
    SizeF(100f, 100f) to smallView,
    SizeF(100f, 200f) to tallView,
    SizeF(200f, 100f) to wideView
  )
  val remoteViews = RemoteViews(viewMapping)

  appWidgetManager.updateAppWidget(id, remoteViews)
}

자바

@Override
public void onUpdate(...) {
  RemoteViews smallView = ...;
  RemoteViews tallView = ...;
  RemoteViews wideView = ...;

  Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
  viewMapping.put(new SizeF(100f, 100f), smallView);
  viewMapping.put(new SizeF(100f, 200f), tallView);
  viewMapping.put(new SizeF(200f, 100f), wideView);
  RemoteViews remoteViews = new RemoteViews(viewMapping);

  appWidgetManager.updateAppWidget(id, remoteViews);
}

정확한 레이아웃 제공

작은 반응형 레이아웃 세트를 만들 수 없다면 위젯이 표시되는 크기에 맞춤설정된 다른 레이아웃을 대신 제공할 수 있습니다. 일반적으로 휴대전화의 경우 두 가지 크기(세로 모드, 가로 모드)이고 폴더블의 경우 네 가지 크기입니다.

이 솔루션을 구현하려면 앱에서 다음 단계를 실행해야 합니다.

  1. 크기 세트가 변경될 때 호출되는 AppWidgetProvider#onAppWidgetOptionsChanged(...)를 오버로드합니다.

  2. 크기가 포함된 Bundle을 반환하는 getAppWidgetManager#getAppWidgetOptions(...)를 호출합니다.

  3. Bundle에서 AppWidgetManager.OPTION_APPWIDGET_SIZES 키에 액세스합니다.

다음 코드 예는 정확한 레이아웃을 제공하는 방법을 보여줍니다.

Kotlin

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }

override fun onAppWidgetOptionsChanged(
  context: Context,
  manager: AppWidgetManager,
  id: Int,
  newOptions: Bundle?
) {
  super.onAppWidgetOptionsChanged(context, manager, id, newOptions)
  // Get the new sizes.
  val sizes = newOptions?.getParcelableArrayList<SizeF>(
    AppWidgetManager.OPTION_APPWIDGET_SIZES
  )
  // Check that the list of sizes is provided by the launcher.
  if (sizes.isNullOrEmpty()) {
    return
  }
  // Map the sizes to the desired RemoteViews
  val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
  appWidgetManager.updateAppWidget(id, remoteViews)
}

자바

// Create the RemoteViews for the given size.
private RemoteViews createRemoteViews(SizeF size) { }

@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager
  appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
      newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the desired RemoteViews.
    Map<SizesF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
      viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

위젯 레이아웃 크기와의 하위 호환성

이전에는 OPTION_APPWIDGET_MIN_WIDTH, OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH, OPTION_APPWIDGET_MAX_HEIGHT 추가 항목을 사용하여 위젯의 크기 범위를 가져와 위젯의 크기를 추정할 수 있었지만 이제 이 로직은 모든 상황에서 작동하지 않습니다. Android 12를 타겟팅하는 위젯의 경우 앞서 설명한 대로 반응형 레이아웃이나 정확한 레이아웃 제공으로 전환하는 것이 좋습니다.

앱의 위젯 선택 도구 환경 개선

Android 12에서는 동적 위젯 미리보기와 위젯 설명을 추가하여 앱의 위젯 선택 도구 환경을 개선할 수 있습니다.

위젯 선택 도구에 확장 가능한 위젯 미리보기 추가

Android 12에서 위젯 선택 도구에 표시되는 위젯 미리보기는 확장 가능한 미리보기로 구성되고 이 미리보기는 위젯의 기본 크기로 설정된 XML 레이아웃으로 제공됩니다. 이전에는 위젯 미리보기가 정적 드로어블 리소스였고 위젯이 홈 화면에 추가된 후 미리보기에서 위젯을 정확하게 반영하지 못하는 경우가 있었습니다.

확장 가능한 위젯 미리보기를 구현하려면 appwidget-provider 요소의 previewLayout 속성을 사용하여 XML 레이아웃을 대신 제공합니다.

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

현실적인 기본값 또는 테스트 값이 있는 실제 위젯과 같은 레이아웃이면 좋습니다.

확장 가능한 위젯 미리보기와의 하위 호환성

Android 11 이하에서 위젯 선택 도구를 사용 설정하여 위젯 미리보기를 표시하려면 previewImage 속성을 계속 지정합니다.

위젯의 모양을 변경하면 미리보기 이미지도 업데이트해야 합니다.

위젯 설명 추가

Android 12에서는 위젯에 표시할 위젯 선택 도구 설명을 선택적으로 제공할 수 있습니다.

대체 텍스트

appwidget-provider의 설명 속성을 사용하여 위젯 설명을 제공합니다.

<appwidget-provider
  ...
  android:description="@string/my_widget_description">
</appwidget-provider>

위젯 설명과의 하위 호환성

앱은 이전 버전의 Android에서 widgetDescription 속성을 사용할 수 있지만 위젯 선택 도구에 표시되지는 않습니다.

더 원활한 전환 사용 설정

Android 12에서 런처는 사용자가 위젯에서 앱을 실행할 때 더 원활한 전환을 제공합니다.

이 개선된 전환을 사용 설정하려면 @android:id/backgroundandroid.R.id.background를 사용하여 배경 요소를 식별합니다.

// Top level layout of the widget.
<LinearLayout
  ...
  android:id="@android:id/background">
</LinearLayout>

더 원활한 전환과의 하위 호환성

앱은 이전 버전의 Android에서 @android:id/background를 사용할 수 있지만 아무런 영향을 미치지 않습니다.

간소화된 RemoteViews 컬렉션 사용

Android 12에서는 앱이 ListView를 채울 때 직접 컬렉션을 전달할 수 있는 setRemoteAdapter(int viewId, RemoteViews.RemoteCollectionItems items) 메서드를 추가합니다. 이전에는 ListView를 사용할 때 RemoteViewsFactory를 반환하도록 RemoteViewsService를 구현하고 선언해야 했습니다.

컬렉션이 일정한 레이아웃 세트를 사용하지 않으면(즉, 일부 항목이 간혹 있는 경우) setViewTypeCount를 사용하여 컬렉션에 포함될 수 있는 고유한 레이아웃의 최대 수를 지정합니다.

다음은 간소화된 RemoteViews 컬렉션을 구현하는 방법을 보여주는 예입니다.

Kotlin

remoteView.setRemoteAdapter(
  R.id.list_view,
  RemoteViews.RemoteCollectionItems.Builder()
    .addItem(/* id= */ ID_1, RemoteViews(...))
    .addItem(/* id= */ ID_2, RemoteViews(...))
    ...
    .setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
    .build()
)

자바

remoteView.setRemoteAdapter(
  R.id.list_view,
  new RemoteViews.RemoteCollectionItems.Builder()
    .addItem(/* id= */ ID_1, new RemoteViews(...))
    .addItem(/* id= */ ID_2, new RemoteViews(...))
    ...
    .setViewTypeCount(MAX_NUM_DIFFERENT_REMOTE_VIEWS_LAYOUTS)
    .build()
);

RemoteViews의 런타임 수정 사용

Android 12에서는 RemoteViews 속성의 런타임 수정을 허용하는 여러 RemoteViews 메서드를 추가합니다. 추가된 메서드의 전체 목록은 RemoteViews API 참조를 확인하세요.

다음 코드 예는 새 메서드를 사용하는 방법을 보여줍니다.

Kotlin

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList())

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP)

자바

// Set the colors of a progress bar at runtime.
remoteView.setColorStateList(R.id.progress, "setProgressTintList", createProgressColorStateList());

// Specify exact sizes for margins.
remoteView.setViewLayoutMargin(R.id.text, RemoteViews.MARGIN_END, 8f, TypedValue.COMPLEX_UNIT_DP);