이전 포스팅에서는 BindingAdapter에 대해서 알아봤다.
InverseBindingAdapter 에 대해 언급을 했었는데 이번엔 InverseBindingAdapter 에 대해 집중해서 포스팅한다.
InverseBindingAdapter 은 양방향 데이터 결합을 지원한다.
갑자기 이게 뭔소린가 싶다면 기존의 BindingAdapter 와 차이가 뭔지를 봐보자.
기존의 Binding 은 Model To View
InverseBinding 은 View To Model 이다. 그러나 InverseBinding 은 Binding의 역할도 포함하고 있기에 양방향이라고 볼 수 있다.
쉽게 말하면 Binding 은 Model 있는 값을 View 로 보내주기만 한다면
InverseBinding 은 view에서 변경된걸 model 에 보내줘야할때 쓰일 수 있다.
내가 spinner에 이걸 도입한 이유도 spinner 에서 선택한 값이 model에서 필요하기 때문이였다.
그럼 다시 이 코드를 보자.
object LocationAdapter {
... 생략
@JvmStatic
@BindingAdapter("selectedValue")
fun Spinner.setSelectedValue(selectedValue: String) {
adapter?.run {
val position =
(adapter as ArrayAdapter<Any>).getPosition(selectedValue)
setSelection(position, false)
tag = position
}
}
// attribute는 바인딩 어뎁터처럼 value을 의미한다. event는 반응할 bindingapdater의 value를 의미한다.
@JvmStatic
@InverseBindingAdapter(attribute = "selectedValue", event = "selectedValueAttrChanged")
fun Spinner.getSelectedValue(): Any? {
return selectedItem
}
// 위에것이 실행되고 아래의 bindingapdater가 실행된다.
@JvmStatic
@BindingAdapter("selectedValueAttrChanged")
fun Spinner.setInverseBindingListener(inverseBindingListener: InverseBindingListener?) {
inverseBindingListener?.run {
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
if (tag != position) {
inverseBindingListener.onChange()
}
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
}
}
}
그냥 보면 어지럽다. 도대체 이게 어떻게 어떤 순서로 동작한다는 말일까?
3가지의 어댑터가 필요한데
일단 InverseBindingApdater 구현체는 get의 역할을 한다.
그리고 event에 해당하는 adapter의 구현체는 change 역할을 한다.
그리고 attribute에 해당하는 adapter의 구현체는 setter의 역할을 한다.
<Spinner
android:id="@+id/locationSpinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:entries="@{viewmodel.spinnerEntry}"
tools:selectedValue="@={viewmodel.spinnerData}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</Spinner>
xml에서는 이런식으로 selectedValue을 사용해서 이걸 호출해낸다.
그러면
@JvmStatic
@InverseBindingAdapter(attribute = "selectedValue", event = "selectedValueAttrChanged")
fun Spinner.getSelectedValue(): Any? {
return selectedItem
}
attribute = "selectedValue" 이 불러내졌으니 event 인 selectedValueAttrChanged 을 실행하게 한다.
그럼
@JvmStatic
@BindingAdapter("selectedValueAttrChanged")
fun Spinner.setInverseBindingListener(inverseBindingListener: InverseBindingListener?) {
inverseBindingListener?.run {
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
parent: AdapterView<*>,
view: View,
position: Int,
id: Long
) {
if (tag != position) {
inverseBindingListener.onChange()
}
}
override fun onNothingSelected(parent: AdapterView<*>) {}
}
}
}
코드가 event로서 실행된다. spinner 의 선택 이벤트가 생겼을때 onChange() 되는것이다.
그럼 xml의 Spinner 안에 tools:selectedValue="@={viewmodel.spinnerData}" 로 인해서
변경된 값이 spinnerData 변수로 들어가게 된다.
아직 내가 이해한 개념이 확실한지는 모르겠다.
공부가 더 필요할 것 같다.
참고
https://jaejong.tistory.com/99
'Android' 카테고리의 다른 글
멀티모듈로 배포할때 파이어베이스 관련 이슈 (0) | 2022.11.06 |
---|---|
Jetpack navigation startDestination 동적으로 설정 (0) | 2022.08.27 |
안드로이드 Paging 3 (0) | 2022.07.24 |
MVVM 에서의 에러처리 전략 (0) | 2022.07.19 |
BindingAdapter에 대해서 (0) | 2022.03.27 |
Android Databinding (데이터 바인딩) (0) | 2022.02.26 |
안드로이드 MVVM에서 코루틴 Flow로 이벤트를 처리하는 방법에 대해 (0) | 2022.02.21 |
안드로이드 클린 아키텍쳐에 대해 (0) | 2022.02.21 |