안드로이드/활용

[Android] 앱 위젯 만들기

damon-911 2023. 9. 12. 22:46

앱 위젯 (App Widget)

앱 위젯은 홈 화면 등에 삽입되어 주기적인 업데이트를 받을 수 있는 소형 애플리케이션 뷰입니다.
이러한 뷰는 AppWidgetProvider를 사용하여 게시할 수 있습니다.
다른 앱 위젯을 포함할 수 있는 애플리케이션 구성요소를 앱 위젯 호스트라고 합니다. 

 

앱 위젯 빌드  |  Android 개발자  |  Android Developers

앱 위젯은 다른 애플리케이션(예: 홈 화면)에 삽입되어 주기적인 업데이트를 받을 수 있는 소형 애플리케이션 뷰입니다. 이러한 뷰는 사용자 인터페이스에서 위젯이라고 하며 앱 위젯 공급자를

developer.android.com

 

앱 위젯 구현하기

1. 앱 위젯의 레이아웃 생성

앱 위젯레이아웃RemoteViews 객체를 통해서만 구성할 수 있습니다. 

또한, 앱 위젯은 다음의 앱 위젯 디자인 가이드라인에 맞춰 작성해야 합니다.

 

앱 위젯 디자인 가이드라인  |  Android 개발자  |  Android Developers

앱 위젯 디자인 가이드라인 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 앱 위젯(경우에 따라 '위젯'이라고도 함)은 Android 1.5에 도입된 기능이며 Android 3.0

developer.android.com

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/weather_widget_background"
    android:gravity="center"
    android:orientation="vertical"
    tools:layout_width="110dp"
    tools:layout_height="40dp">

    <TextView
        android:id="@+id/tv_temperature"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:textColor="@color/white"
        android:textSize="18sp"
        tools:text="-10" />

    <TextView
        android:id="@+id/tv_weather"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:textColor="@color/white"
        android:textSize="12sp"
        tools:text="맑음" />

</LinearLayout>

 

2. AppWidgetProvider 클래스 생성

AppWidgetProvider 클래스는 BroadcastReceiver앱 위젯 브로드캐스트를 처리하기 위한 편의성 클래스로 확장합니다.

또한, 앱 위젯 업데이트, 삭제, 사용 설정 및 사용 중지와 같은 이벤트 브로드캐스트만 수신하여 다음과 같은 메서드를 호출합니다.

  • onUpdate() : 위젯 갱신 주기에 따라 위젯을 갱신할 때 
  • onAppWidgetOptionsChanged() : 위젯이 처음 배치되었거나 위젯의 크기가 변경될 때 
  • onDeleted(Context, int[]) : 위젯이 사용자에 의해 제거될 때
  • onEnabled(Context) : 위젯의 인스턴스가 처음 생성될 때 
  • onDisabled(Context) : 위젯의 마지막 인스턴스가 제거될 때 
  • onReceive(Context, Intent) : 브로드캐스트를 수신할 때 
class WeatherAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)

        appWidgetIds.forEach { appWidgetId ->
            val pendingIntent: PendingIntent = Intent(context, UpdateWeatherService::class.java)
                .let { intent ->
                    PendingIntent.getForegroundService(
                        context,
                        1,
                        intent,
                        PendingIntent.FLAG_IMMUTABLE
                    )
                }

            val views: RemoteViews = RemoteViews(
                context.packageName,
                R.layout.widget_weather
            ).apply {
                setOnClickPendingIntent(R.id.tv_temperature, pendingIntent)
            }

            appWidgetManager.updateAppWidget(appWidgetId, views)
        }

        val serviceIntent = Intent(context, UpdateWeatherService::class.java)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            try {
                ContextCompat.startForegroundService(context, serviceIntent)
            } catch (e: ForegroundServiceStartNotAllowedException) {
                e.printStackTrace()
            }
        } else {
            ContextCompat.startForegroundService(context, serviceIntent)
        }
    }
}

 

3. AppWidgetProviderInfo 리소스 생성

AppWidgetProviderInfo 최소 레이아웃 크기, 초기 레이아웃 리소스, 앱 위젯을 업데이트하는 빈도, 작성 시 실행할 구성 활동(선택사항) 등 앱 위젯의 기본적인 특성을 정의합니다.

XML 리소스에서 단일 <appwidget-provider> 요소를 사용하여 AppWidgetProviderInfo 객체를 정의하고 res/xml 폴더에 저장합니다.

  • initialLayout : 앱 위젯의 레이아웃을 정의하는 레이아웃 리소스를 가리킵니다.
  • minWidth, minHeight : 앱 위젯 디자인 가이드라인에 맞춰서 앱 위젯의 최소 크기를 지정합니다.
  • previewImage : 앱 위젯의 미리보기를 지정합니다. (설정하지 않으면 앱 위젯의 아이콘 사용)
  • resizeMode : 앱 위젯의 크기를 조절할 수 있는 규칙을 지정합니다. (horizontal / vertical / none)
  • updatePeriodMills : AppWidgetProvider업데이트를 요청하는 빈도를 설정합니다. (1초 = 1000)
  • widgetCategory :  앱 위젯을 홈 화면(home_screen), 잠금 화면(keyguard) 또는 둘 다에 표시할지 여부를 선언합니다. Android 5.0 이상에서는 home_screen만 유효합니다.
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_weather"
    android:minWidth="110dp"
    android:minHeight="40dp"
    android:previewImage="@drawable/preview"
    android:resizeMode="none"
    android:updatePeriodMillis="14400000"
    android:widgetCategory="home_screen">
</appwidget-provider>

 

4. Manifest에 앱 위젯 선언

Manifest 파일에 AppWidgetProvider 클래스를 선언합니다.

또한, 메타데이터AppWidgetProviderInfo 리소스를 지정합니다.

<receiver
    android:name="WeatherAppWidgetProvider"
    android:exported="true">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/appwidget_info" />
</receiver>

 

5. 앱 위젯 실행 및 갱신

RemoteView 생성자로 원하는 View를 선택하여 속성을 설정할 수 있습니다.

이 때, PendingIntent를 통해 위젯을 클릭하면 갱신하도록 설정합니다.

이후 AppWidgetManagerupdateAppWidget()을 통해 해당 위젯을 업데이트합니다.

val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(this)

val pendingServiceIntent: PendingIntent =
    Intent(this, UpdateWeatherService::class.java)
        .let { intent ->
            PendingIntent.getService(
                this,
                1,
                intent,
                PendingIntent.FLAG_IMMUTABLE
            )
        }

RemoteViews(packageName, R.layout.widget_weather).apply {
    setTextViewText(
        R.id.tv_temperature,
        current_temperature)
    )
    setTextViewText(
        R.id.tv_weather,
        current_weather
    )
    setOnClickPendingIntent(R.id.tv_temperature, pendingServiceIntent)
}.also { remoteViews ->
    val appWidgetName = ComponentName(this, WeatherAppWidgetProvider::class.java)
    appWidgetManager.updateAppWidget(appWidgetName, remoteViews)
}
728x90
반응형