반응형
Widget을 사용하기 위해 알아야 하는 기본 클래스
1) AppWidgetHost
홈 화면과 같이 UI에 앱 위젯을 삽입하고자 하는 AppWidget 서비스와의 상호작용을 제공하는 클래스이다.
2) AppWidgetHostView
위젯이 표시되어야 할 때마다 래핑되는 프레임입니다.
3) AppWidgetManger
AppWidget의 상태를 업데이트하고 설치된 AppWidget provider에 대한 정보와 그 위젯과 연관된 여러 가지 상태에 대한 정보를 얻을 수 있는 클래스이다.
Launcher 앱에 등록된 위젯 생성 및 관리
위젯을 생성하기 위해서는 다음과 같은 과정으로 이루어집니다.
- 현재 Launcer 앱에 있는 모든 위젯 가져오기
- 해당 widget의 packageName, className을 가지고 ComponetName() 생성
- AppWidgetHost로 id 할당 (allocateAppWidgetId())
- AppWidgetManager를 통해서 id와 Widget을 bind (앱에 위젯을 추가할 수 있는 권한이 있는지 테스트)
- 만약 bind 되지 않았다면 intent를 통해 Activity에게 전달하여 모든 앱 위젯을 추가할 수있는 허용 부여하도록 대화상자 요청 표시 이후 다시 bind
- widget을 보여주기 위한 hostview 생성
- hostview에 widget을 set 해주기
- FrameLayout에 해당 hostView를 add 해주기
전체 코드
다음 코드는 Android 4.1 이상에서 App Widget을 바인딩하는 코드입니다.
🍎 모든 위젯을 가져오기 위한 코드
val allWidgets : List<AppWidgetProviderInfo> = appWidgetManager.installedProviders
🍎 위젯을 생성하고 hostView를 return하는 createWidget 함수
//host와 manager는 applicationContext로 만들기를 권장한다.
val appWidgetManager = AppWidgetManager.getInstance(applicationContext)
val appWidgetHost = AppWidgetHost(applicationcontext, 고유ID)
private fun createWidget(context: Context, name: String): AppWidgetHostView {
//name을 통해서 원하는 WidgetApp의 AppProviderInfo를 가져온다.
val widgetProviderInfo = getWidgetProviderInfo(name)
//ComponentName 생성
val comp = ComponentName(
widgetProviderInfo.provider.packageName,
widgetProviderInfo.provider.className
)
//고유Id를 AppWidgetHost로 부터 할당 받는다.
val widgetId = appWidgetHost.allocateAppWidgetId()
//해당 id를 특정 위젯의 AppWidgetProvider와 host랑 바인딩한다.
val allow = appWidgetManager.bindAppWidgetIdIfAllowed(widgetId, comp)
//바인딩이 안될 시 앱에서 사용자에게 권한을 부여하도록 요청하는 대화상자를 나타내준다.
if (!allow) {
val intent = Intent(AppWidgetManager.ACTION_APPWIDGET_BIND)
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)
intent.putExtra(
AppWidgetManager.EXTRA_APPWIDGET_PROVIDER,
widgetProviderInfo.provider
)
(context as Activity).startActivityForResult(intent, REQUEST_BIND_WIDGET)
//이후 다시 바인딩을 해준다.
appWidgetManager.bindAppWidgetIdIfAllowed(widgetId, comp)
}
//앱 위젯을 그려주기 위한 호스트 뷰를 생성한다.
val hostView =
appWidgetHost.createView(context.applicationContext, widgetId, widgetProviderInfo)
//해당 앱의 id와 AppWidgetProviderInfo를 set해줍니다.
hostView.setAppWidget(widgetId, widgetProviderInfo)
return hostView
}
이렇게 만들어진 hostView를 이제 FrameLayout을 통해서 add 해주면 됩니다.
🍎 ViewModel에서 코드
private val _clockWidget: MutableLiveData<AppWidgetHostView?> = MutableLiveData()
val clockWidget : LiveData<AppWidgetHostView?> = _clockWidget
/*...*/
fun setClockWidget(context : Context, name : String){
_clockWidget.value = createWidget(context, name)
}
🍎 BindingAdapter
@BindingAdapter("app:addWidget")
@JvmStatic
fun addWidget(view: FrameLayout, hostView: AppWidgetHostView) {
//디폴트 패딩 없애주기
hostView.setPadding(0, 0, 0, 0)
view.addView(hostView)
}
🍎 FrameLayout
<FrameLayout
android:id="@+id/clock_widget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/clock_widget_start_margin"
android:layout_marginTop="@dimen/clock_widget_top_margin"
app:addWidget="@{viewModel.clockWidget}"
app:layout_constraintEnd_toStartOf="@+id/side_widget_section"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
하지만 이렇게 하면 아마 위젯이 보이기는 하는데 변경(update)이 제대로 동작을 하지 않을 것입니다. 그러기 위해서는 appWidgetHost가 appWidget이 Change 되었을 때를 받을 수 있게 Listener를 등록해주어야 합니다. 앞서 전체적인 LifeCycle에서 host와 manger를 관리하기 위해서 꼭 만들 때 application context를 사용해주어야 합니다.
//Activity의 onStart()에서 호출
fun startAppWidgetHost() {
appWidgetHost.startListening()
}
//Activit의 onStop()에서 호출
fun stopAppWidgetHost() {
appWidgetHost.startListening()
}
참고
반응형
'개발 > Android' 카테고리의 다른 글
[Android] 여러 개의 LiveData를 한번에 핸들링하는 MediatorLiveData (0) | 2022.10.13 |
---|---|
[Android] 두 개의 RecyclerView 사이에서 Drag and Drop 구현하기 (2) | 2022.09.17 |
[Android] DataBinding이란? (0) | 2022.08.14 |
[Android] The emulator process for AVD has terminated 오류 해결 (0) | 2022.07.09 |
[Android] 백그라운드 작업을 위한 Service (1) - 기본 개념 (0) | 2022.07.05 |
댓글