- Glance에서 어떤 동작을 수행하고 나서 위젯을 업데이트하기 위해 아래와 같은 로직을 호출했다.
CoroutineScope(Dispatchers.IO).launch {
val manager = GlanceAppWidgetManager(context)
widgetList.forEach { (widgetClass, stateKey) ->
val glanceIds = manager.getGlanceIds(widgetClass)
val widgetInstance = widgetClass.getDeclaredConstructor().newInstance()
glanceIds.forEach { glanceId ->
widgetInstance.update(context, glanceId)
}
}
}
- 하지만 데이터는 수정되어도 UI가 갱신되지 않는 문제가 발견되었다.
- 그 이유는 위젯 내부에서 갖고 있는 UiState 자체가 변경되지 않아 데이터는 바뀌었어도 UI는 갱신하겠다는 트리거를 받지 못해서다. Glance는 상태 변화가 없으면 렌더링을 생략한다.
- 이를 위해 PreferencesGlanceStateDefinition 개념을 사용해야 한다.
- PreferencesGlanceStateDefinition은 Glance 위젯에서 상태를 키 - 값 형태로 저장하고 불러오기 위한 기본 저장소 구현체다. 즉, Glance 위젯 전용의 SharedPreferences 같은 역할을 한다고 보면 된다.
- 그리고 키에 해당하는 값을 변경함으로서 하위 컴포저블에 대한 refresh를 진행할 수 있다.
- 위젯 전체 UI를 다시 그리기 위해 provideContent {} 상단에 currentState()로 현재 State 저장 값을 확인한다.
provideContent {
val updatedTime = currentState(key = GlanceUpdateKey) ?: 0L
WidgetLayout(updatedTime)
}
- 이제 currentState 관측 지점부터 UI 갱신이 발생할 수 있게 구성되었다. 단순하게 현재 시간 값을 경신하는 타입을 State로 선언했다.
- GlanceUpdateKey 같은 경우에는 임의의 값이고 object 등으로 정리해두면 파악하기에 용이할 듯하다.
- WidgetLayout 컴포저블 내부에 어떤 버튼이 있고, 이 버튼을 click 하면 RefreshAction을 발생시키도록 하려고 한다. 그러면 아래처럼 ActionClass를 구성해 주면 되겠다.
class RefreshAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
CoroutineScope(Dispatchers.IO).launch {
val manager = GlanceAppWidgetManager(context)
val now = System.currentTimeMillis()
widgetList.forEach { (widgetClass, stateKey) ->
val glanceIds = manager.getGlanceIds(widgetClass)
val widgetInstance = widgetClass.getDeclaredConstructor().newInstance()
glanceIds.forEach { glanceId ->
updateAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) {
it.toMutablePreferences().apply { this[stateKey] = now }
}
widgetInstance.update(context, glanceId)
}
}
}
}
}
- widgetUpdateState를 통해 해당 키 - 값에 접근하여 값을 최신화하고, widgetInstance의 update 호출을 통해 완전한 위젯 갱신을 구현할 수 있다.
- Glance에서 어떤 동작을 수행하고 나서 위젯을 업데이트하기 위해 아래와 같은 로직을 호출했다.
CoroutineScope(Dispatchers.IO).launch {
val manager = GlanceAppWidgetManager(context)
widgetList.forEach { (widgetClass, stateKey) ->
val glanceIds = manager.getGlanceIds(widgetClass)
val widgetInstance = widgetClass.getDeclaredConstructor().newInstance()
glanceIds.forEach { glanceId ->
widgetInstance.update(context, glanceId)
}
}
}
- 하지만 데이터는 수정되어도 UI가 갱신되지 않는 문제가 발견되었다.
- 그 이유는 위젯 내부에서 갖고 있는 UiState 자체가 변경되지 않아 데이터는 바뀌었어도 UI는 갱신하겠다는 트리거를 받지 못해서다. Glance는 상태 변화가 없으면 렌더링을 생략한다.
- 이를 위해 PreferencesGlanceStateDefinition 개념을 사용해야 한다.
- PreferencesGlanceStateDefinition은 Glance 위젯에서 상태를 키 - 값 형태로 저장하고 불러오기 위한 기본 저장소 구현체다. 즉, Glance 위젯 전용의 SharedPreferences 같은 역할을 한다고 보면 된다.
- 그리고 키에 해당하는 값을 변경함으로서 하위 컴포저블에 대한 refresh를 진행할 수 있다.
- 위젯 전체 UI를 다시 그리기 위해 provideContent {} 상단에 currentState()로 현재 State 저장 값을 확인한다.
provideContent {
val updatedTime = currentState(key = GlanceUpdateKey) ?: 0L
WidgetLayout(updatedTime)
}
- 이제 currentState 관측 지점부터 UI 갱신이 발생할 수 있게 구성되었다. 단순하게 현재 시간 값을 경신하는 타입을 State로 선언했다.
- GlanceUpdateKey 같은 경우에는 임의의 값이고 object 등으로 정리해두면 파악하기에 용이할 듯하다.
- WidgetLayout 컴포저블 내부에 어떤 버튼이 있고, 이 버튼을 click 하면 RefreshAction을 발생시키도록 하려고 한다. 그러면 아래처럼 ActionClass를 구성해 주면 되겠다.
class RefreshAction : ActionCallback {
override suspend fun onAction(
context: Context,
glanceId: GlanceId,
parameters: ActionParameters
) {
CoroutineScope(Dispatchers.IO).launch {
val manager = GlanceAppWidgetManager(context)
val now = System.currentTimeMillis()
widgetList.forEach { (widgetClass, stateKey) ->
val glanceIds = manager.getGlanceIds(widgetClass)
val widgetInstance = widgetClass.getDeclaredConstructor().newInstance()
glanceIds.forEach { glanceId ->
updateAppWidgetState(context, PreferencesGlanceStateDefinition, glanceId) {
it.toMutablePreferences().apply { this[stateKey] = now }
}
widgetInstance.update(context, glanceId)
}
}
}
}
}
- widgetUpdateState를 통해 해당 키 - 값에 접근하여 값을 최신화하고, widgetInstance의 update 호출을 통해 완전한 위젯 갱신을 구현할 수 있다.