코린이 개발로그
Android ListView Widget을 만들어보자 본문
위젯은 다른 애플리케이션에 삽입되어 주기적인 업데이트를 받을 수 있는 소형 애플리케이션 뷰입니다.
앱 위젯을 만들기 위해 다음과 같은 단계가 있습니다.
- Manifest에 위젯과 리시버 등록하기.
- AppWidgetProviderInfo객채 구현
- AppWidgetProvider를 상속받은 클래스 구현
- widget layout 구현
이 모든 과정을 끝내야 위젯이 정상적으로 작동하게 됩니다. 현재는 이 과정을 따로 하지 않아도 Android Studio에서 New > Widget > AppWidget을 통해 위 과정을 한 번에 진행할 수 있습니다.
Manifest에서 위젯 선언하기
<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>
여기서 name에 해당하는 속성은 직접 만들어준 AppWidgetProvider를 상속받은 클래스입니다.
또한 intent-filter 에서 위젯의 업데이트를 receiver를 통해 받고 있습니다.
<meta-data>는 AppWidgetProviderInfo를 가리키고 있습니다. 다음 단계에서 만들어줄 위젯의 정보와 관련된 resource파일입니다.
AppWidgetProviderInfo 만들기
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp" //최소 넓이
android:minHeight="40dp" //최소 길이
android:updatePeriodMillis="86400000" //업데이트 주기 86400000 = 하루
android:previewImage="@drawable/preview" //위젯에서 보여줄 이미지
android:initialLayout="@layout/example_appwidget" //위젯에 사용 될 레이아웃
android:resizeMode="horizontal|vertical"//화면에서 가로/세로 수정가능 여부
android:widgetCategory="home_screen">
</appwidget-provider>
여기서 사용되는 minWidth와 minHeight는 각각
셀 개수 | 사용 가능한 크기(dp) |
1 | 40dp |
2 | 110dp |
3 | 180dp |
4 | 250dp |
... | ... |
n | 70 x n - 30 |
으로 계산할 수 있습니다.
뷰의 관한 자세한 내용은 아래 링크에서 확인할 수 있습니다.
앱 위젯 디자인 가이드라인 | Android 개발자 | Android Developers
앱 위젯 디자인 가이드라인 앱 위젯(경우에 따라 '위젯'이라고도 함)은 Android 1.5에 도입된 기능이며 Android 3.0 및 3.1에서 크게 개선되었습니다. 위젯은 애플리케이션의 정보를 적시에 또는 가장
developer.android.com
Widget Layout 만들기
이제 실제로 사용하게 될 위젯의 layout을 만들차례입니다.
앱에서 사용되는 View들과 달리 위젯을 RemoteView를 기반으로 사용되고 있습니다.
보통 Layout에서 많이 사용되는 ConstraintLayout이지만 위젯에서는 사용이 불가능합니다. 사용 가능한 Layout은 :
- FrameLayout
- LinearLayout
- RelativeLayout
- GridLayout
들을 지원하고 있으며 해당 Layout안에서 사용가능한 Class는
- AnalogClock
- Button
- Chronometer
- ImageButton
- ImageVIew
- ProgressBar
- TextView
- ViewFlipper
- ListView
- GridView
- StackView
- AdapterViewFlipper
를 지원하고 있습니다.
또한 많이 사용되는 Recyclerview를 사용하게 되면 에러가 뜨며 위젯 생성에 문제가 있게 됩니다.
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/widget_margin">
<ListView
android:id="@+id/lv_widget_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@android:color/transparent"
android:paddingVertical="4dp"
/>
</FrameLayout>
현재 RecyclerView를 사용할 수 없기 때문에 ListView로 만들어 주었습니다.
AppWidgetProvider 상속받은 클래스 생성
AppWidgetProvider는 BroadcastReceiver를 상속받고 있는 클래스로, 위젯의 브로드캐스트를 처리하기 위한 클래스입니다. 이 클래스는 위젯의 업데이트, 삭제, 생성, 설정 등등의 작업에 사용되고 있으며 이벤트들을 수신하는 데 사용됩니다.
AppWidgetProvider는 다음과 같은 함수들을 가지고 있습니다.
위의 메타데이터에서 지정한 updatePeriodMillis속성에 의해 지정된 시간 간격마다 위젯을 업데이트하기 위해 호출됩니다. 또한 이 함수는 사용자가 위젯을 처음으로 설치할 때 불려지게 됩니다.
위젯이 처음으로 설치될 때 혹은 위젯의 크기에 변경이 있을 때 불려지는 함수입니다.
BroadcastReceiver를 상속받기 때문에 브로드캐스트를 필터링하여 자신과 부합하는 호출에 응답을 합니다.
위젯에서 가장 중요한건 onUpdate()함수입니다. 위젯이 생성될때 실행 되기때문에 원하는 방식으로 이곳에서 해당 위젯을 초기화 시켜줄 수 있습니다. 현재 AppWidgetProvider에 등록된 위젯의 메타데이터는 하나 뿐이기때문에 원하는 방식으로 초기화 해주고 있습니다. 또한 클릭 이벤트같은 이벤트들은 모두 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 {
val serviceIntent = Intent(context, MyRemoteViewsService::class.java)
val widget = RemoteViews(context.packageName, R.layout.widget)
widget.setRemoteAdapter(R.id.lv_widget_list, serviceIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
만약 보통의 위젯이었다면 그저 해당 위젯의 layout을 불러와 적용시켜주면 됐지만, 현재 만들고 싶은 ListView 위젯은 말 그대로 ListView이기 때문에 ListAdapter가 필요합니다, 하지만 위에서도 말했듯이 위젯은 RemoteView를 기반으로 하고 있기에 따로 이에 해당하는 Adapter를 만들어 주어야 합니다.
그래서 MyRemoteViewsService라는 객채를 생성하여 Adapter를 대신하여 Item을 생성해 줄 것입니다.
class MyRemoteViewsService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent?): RemoteViewsFactory {
return ContactRemoteViewsFactory(this.applicationContext)
}
}
class ContactRemoteViewsFactory(
private val context: Context
) : RemoteViewsService.RemoteViewsFactory {
private var data = listOf(1,2,3,4,5)
override fun onCreate() {}
override fun onDataSetChanged() {
//data 가 바뀌었다는 호출을 받았을 때 실행되는 함수
}
override fun onDestroy() {}
override fun getCount() = data.size
override fun getViewAt(position: Int): RemoteViews {
val listviewWidget = RemoteViews(context.packageName, R.layout.item_widget)
// ListView에 들어갈 item 뷰를 따로 생성해 줬습니다.
val number = data[position]
setTextViewText(R.id.title, number)// 해당 item의 속성을 변경해줍니다.
return listviewWidget
}
override fun getLoadingView(): RemoteViews? {
return null
}
override fun getViewTypeCount(): Int {
return 1
}
override fun getItemId(position: Int): Long {
return 0
}
override fun hasStableIds(): Boolean {
return false
}
}
위의 코드대로 아이템을 지정하여 ListView의 아이템을 생성해줄 수 있습니다.
이렇게 하여 ListView Widget의 생성과정을 알아보았습니다.
'Kotlin & Android' 카테고리의 다른 글
Compose ViewModel (0) | 2025.02.26 |
---|---|
DI 와 Hilt 와 Repository패턴 (0) | 2021.11.29 |
AAC 와 MVVM (0) | 2021.11.09 |
FireBase Gradle build 오류 (2) | 2021.04.24 |