투두, 메모, 디데이 기능을 하나의 앱 안에서 모두 사용할 수 있는 간단한 앱을 만드는 데 1주일이 걸렸다. compose의 매력을 즐기면서 간단하고 빠르게 구현할 수 있어서 즐거웠는데... 가장 주요 목적이었던 위젯 만드는 부분에서 완전히 꽉 막히고 말았다. 이왕이면 compose로 만들고 싶어서 시작한 것이었는데, 자료도 많지 않았고 있어도 단순히 정말 위젯을 만들기만 하는 코드들이었다.
그래서 그냥 자료 많은 방법으로 갈까도 하다가 이왕 시작한 거 어떻게 해서든지 끝까지 해내보겠다는 마음으로 공부를 시작해 본다.
아래 url은 공식문서의 glance파트에 있는 코드 샘플 깃허브이다.
https://github.com/android/user-interface-samples/tree/main/AppWidget
GitHub - android/user-interface-samples: Multiple samples showing the best practices in the user interface on Android.
Multiple samples showing the best practices in the user interface on Android. - GitHub - android/user-interface-samples: Multiple samples showing the best practices in the user interface on Android.
github.com
버튼, 이미지, 리스트, 날씨 위젯에 대한 코드를 제공해 주고 있는데,
여기에 있는 코드 샘플을 분석을 해서 오래걸리더라도 나만의 위젯 만들기를 해보려고 한다.
첫 번째로
https://github.com/android/user-interface-samples/blob/main/AppWidget/app/src/main/java/com/example/android/appwidget/glance/list/ListGlanceWidget.kt 이 코드를 분석해 봐야겠다. ListGlanceWidget이다.
옆 결과를 만들기 위한 코드이다.
이 코드는 크게 다섯 덩어리로 되어 있다.
- class ListGlanceWidget : GlanceAppWidget()
- private fun CheckBoxItem(id: Int)
- private fun CountChecked()
- class CheckboxClickAction : ActionCallback
- class ListGlanceWidgetReceiver : GlanceAppWidgetReceiver()
여기에 아래 변수가 선언 되어 있다.
private val toggledStringIdKey = ActionParameters.Key<String>("ToggledStringIdKey")
private val groceryStringIds = listOf(
R.string.grocery_list_milk,
R.string.grocery_list_eggs,
R.string.grocery_list_tomatoes,
R.string.grocery_list_bacon,
R.string.grocery_list_butter,
R.string.grocery_list_cheese,
R.string.grocery_list_potatoes,
R.string.grocery_list_broccoli,
R.string.grocery_list_salmon,
R.string.grocery_list_yogurt
)
ActionParameters.Key <> 이거는 Action 정보를 넘겨줄 때 사용한다고 한다.
1번의 class ListGlanceWidget : GlanceAppWidget()에서는 위젯의 ui를 구성했다.
class ListGlanceWidget : GlanceAppWidget() {
@Composable
override fun Content() {
GlanceTheme {
Column(
//Colum modifier
) {
Text(텍스트와 텍스트 설정)
CountChecked() //3번 덩이리
LazyColumn {
items(groceryStringIds) { id ->
CheckBoxItem(id) //2번 덩어리
}
}
}
}
}
}
위젯 상단 제목을 Text에서 구성해주고,
CountChecked() 함수를 사용해 지금까지 체크된 리스트의 개수를 출력해 주고,
LazyColumn을 사용해서 아이템 리스트를 출력해 주었다.
2. private fun CheckBoxItem(id: Int)에서는 위에서 언급했듯 체크된 리스트 출력이다,
currentState는 상태 데이터를 찾는기능을 하는데, <Preference>의 데이터를 가져오기 위한 것으로 추정한다.
booleanPreferenceKey는 Boolean preference의 키를 가져오는 기능을 한다.
@Composable
private fun CheckBoxItem(id: Int) {
val prefs = currentState<Preferences>()
val checked = prefs[booleanPreferencesKey(id.toString())] ?: false
CheckBox(
text = LocalContext.current.getString(id), //이거는 R어쩌구 값을 glance에서 가져오는 방법
checked = checked,
//체크박스 체크했을 때 반응
onCheckedChange = actionRunCallback<CheckboxClickAction>(
actionParametersOf(
toggledStringIdKey to id.toString(),
)
),
modifier = GlanceModifier.padding(12.dp),
style = TextStyle(color = GlanceTheme.colors.textColorPrimary),
)
}
3. private fun CountChecked() 도 체크된 리스트의 개수를 출력 함수이다.
@Composable
private fun CountChecked() {
val prefs = currentState<Preferences>()
val checkedCount = groceryStringIds.filter {
prefs[booleanPreferencesKey(it.toString())] ?: false
}.size //체크 된 것들의 개수를 가져오는 것
Text(
text = "$checkedCount checkboxes checked",
modifier = GlanceModifier.padding(start = 8.dp),
style = TextStyle(
color = GlanceTheme.colors.textColorSecondary
)
)
}
4, class CheckboxClickAction : ActionCallback 은 2번에서 체크박스 클릭했을 때 action을 구현하는 class이다.
class CheckboxClickAction : ActionCallback {
override suspend fun onAction(context: Context, glanceId: GlanceId, parameters: ActionParameters) {
val toggledStringId = requireNotNull(parameters[toggledStringIdKey]) {
"Add $toggledStringIdKey parameter in the ActionParameters."
}
// The checked state of the clicked checkbox can be added implicitly to the parameters and
// can be retrieved by using the ToggleableStateKey
val checked = requireNotNull(parameters[ToggleableStateKey]) {
"This action should only be called in response to toggleable events"
}
updateAppWidgetState(context, glanceId) { state ->
state[booleanPreferencesKey(toggledStringId)] = checked
}
ListGlanceWidget().update(context, glanceId)
}
}
requireNotNull 은 전달받은 파라미터 값이 Null이 아니라면 결과 값을 변수에 할당하고, Null일 경우 IllegalArgumentException을 발생시킨다.
그러니까, toggledStringId변수에 null이 아니면 값을 할당하고
null이면 throw IllegalArgumentException("Add $toggledStringIdKey parameter in the ActionParameters.")
이렇게 작동을 한다.
5. 마지막은 Receiver 선언 class이다.
class ListGlanceWidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = ListGlanceWidget()
}
내가 지금 하고 싶은 것은 이 메모앱에서 사용자가 room db에 저장한 여러 개의 아이템 중 하나를 골라 위젯으로 만들거나 여러 개를 한 번에 보여주는 위젯을 만드는 것이었는데, 아직 위젯을 추가했을 때 선택하는 화면으로 넘어가서 특정 아이템만 보여주는 방법을 모르니까 한 번에 싹 다 가져오는 것부터 연습 삼아 만들어 봐야겠다.
내가 현재 작업하려는 구성은 위 코드보다는 간단하다.
사용자의 db에 있는 DDAY리스트를 가져오고, 액션없이 출력만 해주면 된다.
우선 어떻게 가져와야 하는지 몰라서 예시 list를 만들어 구현을 시작했다.
private val itemIds = listOf(
"Aday","Bday","Cday","Dday"
)
class Widget : GlanceAppWidget() {
@Composable
override fun Content() {
LazyColumn{
items(itemIds){id->
DDayItem(name =id)
}
}
}
}
class WidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = Widget()
}
@Composable
fun DDayItem(name:String) {
Column() {
Text(text = name, style = TextStyle(color = ColorProvider(Color.White)))
Spacer(modifier = GlanceModifier.height(10.dp))
// Text(text = calDate(date), style = TextStyle(fontSize = 16.sp))
}
}
그다음으로 는 디데이니까 이름만 있는 것이 아니기 때문에
class item(val name :String , val date :String)
private val itemIds = listOf(
item("Aday","2023-5-1"),
item("Bday","2023-6-1"),
item("Cday","2023-7-1")
)
class Widget : GlanceAppWidget() {
@Composable
override fun Content() {
LazyColumn{
items(itemIds){
DDayItem(name =it.name, date = it.date)
}
}
}
}
class WidgetReceiver : GlanceAppWidgetReceiver() {
override val glanceAppWidget: GlanceAppWidget = Widget()
}
@Composable
fun DDayItem(name:String,date: String) {
Row() {
Text(text = name, style = TextStyle(color = ColorProvider(Color.White)))
Spacer(modifier = GlanceModifier.width(30.dp))
Text(text = calDate(date), style = TextStyle(fontSize = 16.sp))
}
}
마지막으로 남은 일은 db안에 있는 정보를 가져오는 건데...
이거는 다른 샘플 코드를 분석해 보면 감이 올 것도 같다..
다음 게시물에 이어서 !
'안드로이드 앱 개발 공부 > jetpack Compose' 카테고리의 다른 글
도통 모르겠어서 기록하면서 공부하는 Jetpack Compose_ glance 코드 샘플 분석 후 적용하기까지 (2) (0) | 2023.03.21 |
---|---|
[jetpack compose] 제트팩 컴포즈로 탭 만들기 (1) | 2023.03.07 |
JetPack Compose_Scaffold, TextField, Button, 구조분해, SnackBar, 코루틴 스코프 (0) | 2023.01.09 |
JetPack Compose_Image, Card, 상태 (0) | 2023.01.04 |
JetPack Compose_ 리스트, LazyColumn (0) | 2023.01.04 |
댓글