Android

[Android] Widget 에서 ListView 사용하여 데이터 노출하기

실버☆ 2023. 10. 12. 11:23

 

 

 

💡 위젯 구성


  • AppWidgetProvidernfo - 위젯의 메타데이터 설명
  • AppWidgetProvider - 프로그래밍을 통한 기본 메서드 정의
  • view (xml) - 초기 레이아웃 정의

 

💡 AppWidgetProvider


AppWidgetProvider 클래스는 BroadcastReceiver 를 확장한 클래스로, 위젯과 관련된 이벤트 브로드캐스트를 수신한다.

  • onUpdate(): 위젯을 업데이트하기 위해 호출. 이벤트 핸들러 등 필수 설정을 수행해야 한다.

리스트뷰가 포함된 위젯을 만들려면 onUpdate() 메서드 내에 setRemoteAdapter() 를 호출함으로써, 데이터를 불러와 리스트뷰에 바인딩시킬 수 있도록 구현한다.

 

class NewAppWidget : AppWidgetProvider() {
    override fun onUpdate(
        context: Context,
        appWidgetManager: AppWidgetManager,
        appWidgetIds: IntArray
    ) {
        // There may be multiple widgets active, so update all of them
        for (appWidgetId in appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId)
            super.onUpdate(context, appWidgetManager, appWidgetIds)
        }
    }
}

internal fun updateAppWidget(
    context: Context,
    appWidgetManager : AppWidgetManager,
    appWidgetId: Int
) {
    val widgetText = context.getString(R.string.appwidget_text)
    val intent = Intent(context, MyWidgetService::class.java)
    // Construct the RemoteViews object
    val views = RemoteViews(context.packageName, R.layout.new_app_widget).apply {
        setTextViewText(R.id.widget_title, widgetText)
        setRemoteAdapter(R.id.widget_list, intent)
    }
    // Instruct the widget manager to update the widget
    appWidgetManager.updateAppWidget(appWidgetId, views)
}

 

 

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>

 

제공자 클래스는 AndroidManifest.xml <receiver> 내에 정의되어야 하며, <intent-filter> 속성을 포함시키므로 브로드캐스트를 허용하도록 지정한다.

 

 

 

💡 RemoteViewsService


위젯 레이아웃은 늘 RemoteViews를 기반으로 한다. 그렇기에 지원되는 레이아웃과 뷰에 제한이 있다. 그래서 이번엔 ListView를 사용해 위젯을 구성해 보는 방법을 알아보겠다. 

 

앱 위젯에서 리스트뷰를 사용하려면 RemoteViewsService를 사용하여 원격 데이터를 불러와야 한다. 컬렉션에 데이터를 표시하려면 Adapter를 사용하여 화면에 데이터를 바인딩해야 하는데, 앱 위젯 context에서 Adapter는 Adapter를 감싸고 있는 RemoteViewsFactory로 대체된다.

이때 RemoteViewsService는 원격 어댑터가 RemoteViews 객체를 요청할 수 있는 서비스이다.

 

class MyWidgetService : RemoteViewsService() {

    override fun onGetViewFactory(p0: Intent): RemoteViewsFactory {
        return MyRemoteViewsFactory(this.applicationContext)
    }
}

 

RemoteViewsFactory 클래스는 리스트뷰 항목에 대한 데이터를 위젯에 제공한다. 즉 데이터를 원격으로 리스트뷰에 연결하는 어댑터 역할을 하는 것이다.

 

class MyRemoteViewsFactory(
    private val context: Context
): RemoteViewsService.RemoteViewsFactory {

    private lateinit var widgetItems: List<String>

    override fun onCreate() {
        widgetItems = listOf("1", "2", "3")
    }

    override fun getViewAt(p0: Int): RemoteViews {
        val remoteViews = RemoteViews(context.packageName, R.layout.widget_list_item).apply {
            setTextViewText(R.id.widget_item_title, widgetItems[p0])
        }
        return remoteViews
    }

    /** implement method */
}

 

처음 Factory 생성 시, 시스템이 onCreate() 를 생성한다. 이때 데이터 아이템 개체 초기화 및 데이터 연결에 대한 부분을 수행할 수 있다. 위젯은 여기서 초기화된 배열의 인덱스에 접근하여 데이터를 표시한다.

 

AndroidManifest.xml

<service android:name="MyWidgetService"
    android:permission="android.permission.BIND_REMOTEVIEWS">
</service>

 

manifest 파일에 해당 서비스를 선언해준다. BIND_REMOTEVIEWS 권한을 사용하여 다른 애플리케이션이 위젯 데이터에 접근하는 것을 방지할 수 있다.

 

 

 

여기까지 하면, 위젯을 띄웠을 때 임의로 넣은 데이터가 리스트뷰에 표시되는 것을 볼 수가 있다.