[Android] 위젯 만들기 #1

이제일·2022년 11월 5일
5

Android

목록 보기
10/15
post-thumbnail

Widget

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

기본 준비 사항

  • AppWidgetProvider 클래스 구현
    브로드캐스트 이벤트를 기반으로 앱 위젯과의 상호작용 정의

  • AppWidgetProviderInfo (xml)
    앱 위젯의 레이아웃, 업데이트 빈도, AppWidgetProvider 클래스 등 앱 위젯의 메타데이터 설정

  • 레이아웃 파일
    위젯의 레이아웃 정의

  • Manifest 설정
    AppWidgetProvider 클래스 파일 정의 및 브로드캐스트(위젯 이벤트) 수신 설정

Manifest

위젯 프로바이더가 위젯과 상호작용을 위해서 브로드캐스트를 받도록 아래와 같이 설정한다

<manifest ...>
    <application ... >
    ...
    
    <receiver android:name="ExampleAppWidgetProvider" >
        <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>
    
    ...
    </application>
</manifest>

receiver - name : 설정할 리시버인 위젯 프로바이더를 정의한 클래스
action 태그 : 위젯이 보내는 브로드 캐스트가 정의되어 있다
meta-data - name : 프로바이더의 데이터를 AppWidgetProviderInfo 설명자로 표시하려면 위와 같이 설정
meta-data - resource : 위젯과 관련된 설정이 포함된 XML 파일이다

AppWidgetProviderInfo

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

XML 리소스에서 단일 <appwidget-provider> 요소를 사용하여 AppWidgetProviderInfo 객체를 정의한다.

프로젝트의 res/xml/ 폴더에 저장한다.

      <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
        android:minWidth="40dp"
        android:minHeight="40dp"
        android:updatePeriodMillis="86400000"
        android:previewImage="@drawable/preview"
        android:initialLayout="@layout/example_appwidget"
        android:configure="com.example.android.ExampleAppWidgetConfigure"
        android:resizeMode="horizontal|vertical">
    </appwidget-provider>
    

minWidth, minHeight : 위젯의 최소 크기를 지정한다. 이에 따라 기본 사이즈가 정해진다.
updatePeriodMillis : 앱이 자동으로 업데이트 되는 주기(ms)를 정한다(최소 30분) 위젯 프로바이더의 onUpdate()를 호출함
previewImage : 위젯 생성 전 미리 보는 이미지, 없을 시 앱 아이콘으로 표시된다.
initialLayout : 앱 위젯의 레이아웃을 지정
configure : 위젯을 추가할 때 속성을 구성하기 위한 Activity 정의, 선택사항
resizeMode : 사용자가 홈 화면 위젯의 크기를 조절 할 수 있는 방향 정의, 모두 조절 가능 할 경우 horizontal|vertical

위젯 크기 설정

위젯의 기본 셀 크기를 지정할 때의 가이드 라인입니다.

android:minWidth="40dp"
android:minHeight="40dp"

위 처럼 크기를 지정할 경우 1x1의 위젯을 제공합니다.

아래 표를 사용하여 원하는 수의 그리드 셀에 따라 위젯의 최소 크기를 대략적으로 추정할 수 있습니다.

Layout

앱 위젯의 초기 레이아웃을 XML로 정의하고 프로젝트의 res/layout/ 디렉터리에 저장해야 한다.
구글이 제공하는 앱 위젯 디자인 가이드라인은 다음과 같다.

위젯 레이아웃은 RemoteViews기반이기에 사용할 수 있는 뷰들은 다음과 같다.

Layout

  • FrameLayout
  • LinearLayout
  • RelativeLayout
  • GridLayout

View

  • AnalogClock
  • Button
  • Chronometer
  • ImageButton
  • ImageView
  • ProgressBar
  • TextView
  • ViewFlipper
  • ListView
  • GridView
  • StackView
  • AdapterViewFlipper

이러한 클래스의 하위 항목 또한 지원하지 않는다.

AppWidgetProvider

AppWidgetProvider 클래스는 BroadcastReceiver를 앱 위젯 브로드캐스트를 처리하기 위한 편의성 클래스로 확장한다.
위젯 업데이트, 삭제, 사용 설정 및 중지와 같은 이벤트 브로드 캐스트만을 수신해서 메서드를 호출한다.

class ExampleAppWidgetProvider : AppWidgetProvider() {

    // 앱 위젯은 여러개가 등록 될 수 있는데, 최초의 앱 위젯이 등록 될 때 호출 됩니다. (각 앱 위젯 인스턴스가 등록 될때마다 호출 되는 것이 아님)
    override fun onEnabled(context: Context) {
        super.onEnabled(context)
    }

    // onEnabled() 와는 반대로 마지막의 최종 앱 위젯 인스턴스가 삭제 될 때 호출 됩니다
    override fun onDisabled(context: Context) {
        super.onDisabled(context)
    }
    
    // android 4.1 에 추가 된 메소드 이며, 앱 위젯이 등록 될 때와 앱 위젯의 크기가 변경 될 때 호출 됩니다.
    // 이때, Bundle 에 위젯 너비/높이의 상한값/하한값 정보를 넘겨주며 이를 통해 컨텐츠를 표시하거나 숨기는 등의 동작을 구현 합니다
    override fun onAppWidgetOptionsChanged(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int, newOptions: Bundle) {
        super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions)
    }

    // 위젯 메타 데이터를 구성 할 때 updatePeriodMillis 라는 업데이트 주기 값을 설정하게 되며, 이 주기에 따라 호출 됩니다.
    // 또한 앱 위젯이 추가 될 떄에도 호출 되므로 Service 와의 상호작용 등의 초기 설정이 필요 할 경우에도 이 메소드를 통해 구현합니다
    override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
        super.onUpdate(context, appWidgetManager, appWidgetIds)
    }

    // 이 메소드는 앱 데이터가 구글 시스템에 백업 된 이후 복원 될 때 만약 위젯 데이터가 있다면 데이터가 복구 된 이후 호출 됩니다.
    // 일반적으로 사용 될 경우는 흔치 않습니다.
    // 위젯 ID 는 UID 별로 관리 되는데 이때 복원 시점에서 ID 가 변경 될 수 있으므로 백업 시점의 oldID 와 복원 후의 newID 를 전달합니다
    override fun onRestored(context: Context, oldWidgetIds: IntArray, newWidgetIds: IntArray) {
        super.onRestored(context, oldWidgetIds, newWidgetIds)
    }

    // 해당 앱 위젯이 삭제 될 때 호출 됩니다
    override fun onDeleted(context: Context, appWidgetIds: IntArray) {
        super.onDeleted(context, appWidgetIds)
    }

    // 앱의 브로드캐스트를 수신하며 해당 메서드를 통해 각 브로드캐스트에 맞게 메서드를 호출한다.
    override fun onReceive(context: Context, intent: Intent) {
        super.onReceive(context, intent)
    }
}

중요한 메서드는 onUpdate()
각 위젯이 등록 될 때마다 호출되고, AppWidgetProviderInfo에서 설정한 업데이트 주기마다 호출된다.
따라서 사용자 상호작용 이벤트를 허용하는 경우 이 콜백에서 이벤트 핸들러를 등록한다.

위젯 클릭 시 Activity 실행

클릭 시 이벤트를 등록할 부분을 상술한 바와 같이 onUpdate()에서 등록한 예제이다.

    class ExampleAppWidgetProvider : AppWidgetProvider() {

        override fun onUpdate(
                context: Context,
                appWidgetManager: AppWidgetManager,
                appWidgetIds: IntArray
        ) {
            // Perform this loop procedure for each App Widget that belongs to this provider
            appWidgetIds.forEach { appWidgetId ->
                // Create an Intent to launch ExampleActivity
                val pendingIntent: PendingIntent = Intent(context, ExampleActivity::class.java)
                        .let { intent ->
                            PendingIntent.getActivity(context, 0, intent, 0)
                        }

                // Get the layout for the App Widget and attach an on-click 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 app widget
                appWidgetManager.updateAppWidget(appWidgetId, views)
            }
        }
    }
    

각 위젯은 등록될 때마다 ID를 부여받는다.

이를 appWidgetIds에 받고 해당 ID마다 각 뷰에 이벤트를 등록해 PendingIntent를 통해 액티비티를 실행하게된다.
PendingIntent.getactivity(context, requestCode, Intent, Flags)에서 Flags가 안될 경우 다음 참조

RemoteViews생성자로 해당 View를 선택해 리스너 및 속성을 설정할 수 있다.

이후 AppWidgetManagerupdateAppWidget함수를 이용해 해당 위젯을 업데이트한다.
또는 partiallyUpdateAppWidget함수를 통해 일부분 업데이트를 한다.


레퍼런스

https://developer.android.com/guide/topics/appwidgets?hl=ko
https://blog.naver.com/PostView.naver?blogId=pistolcaffe&logNo=222237065122&redirect=Dlog&widgetTypeCall=true&directAccess=false

profile
세상 제일 이제일

0개의 댓글