Data Binding - leekiljae2019/MyKotlinDocs GitHub Wiki

1. DataBinding์ด๋ž€?

Android API Level 7 ์ดํ›„๋ถ€ํ„ฐ๋Š” ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์ง€์›ํ•œ๋‹ค. DataBinding์ด๋ž€ xml์— ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ”์ธ๋”ฉํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ์ฝ”๋“œ๋ฅผ ์ค„์ด๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ, ๋ณดํ†ต MVP or MVVM ํŒจํ„ด์„ ๊ตฌํ˜„ ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค. DataBinding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋งคํ‹ฑ ๋ฐฉ์‹์ด ์•„๋‹ˆ๋ผ ์„ ์–ธ์  ํ˜•์‹์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์˜ UI ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์•ฑ์˜ ๋ฐ์ดํ„ฐ ์†Œ์Šค์™€ ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์› ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ๋ ˆ์ด์•„์›ƒ์€ ํ”ํžˆ UI ํ”„๋ ˆ์ž„์›Œํฌ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ํ™œ๋™์—์„œ ์ •์˜๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜ ์ฝ”๋“œ๋Š” findViewById()๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ TextView ์œ„์ ฏ์„ ์ฐพ์•„ viewModel ๋ณ€์ˆ˜์˜ userName ์†์„ฑ์— ๊ฒฐํ•ฉํ•˜๋Š” ์˜ˆ์ด๋‹ค.

findViewById(R.id.sample_text).apply {         text = viewModel.userName }

ํ•˜์ง€๋งŒ DataBinding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์œ„์˜ Kotlin ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค.

๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์—์„œ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ฒฐํ•ฉํ•˜๋ฉด ํ™œ๋™์—์„œ ๋งŽ์€ UI ํ”„๋ ˆ์ž„์›Œํฌ ํ˜ธ์ถœ์„ ์‚ญ์ œํ•  ์ˆ˜ ์žˆ์–ด ํŒŒ์ผ์ด ๋”์šฑ ๋‹จ์ˆœํ™”๋˜๊ณ  ์œ ์ง€๊ด€๋ฆฌ ๋˜ํ•œ ์‰ฌ์›Œ์ง€๋ฉฐ ์•ฑ ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋˜๊ณ  ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๋ฐ null ํฌ์ธํ„ฐ ์˜ˆ์™ธ๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๋‹ค.

2. DataBinding ์‚ฌ์šฉํ•˜๊ธฐ

2.1. DataBinding ์„ค์ • build.gradle ์ƒ๋‹จ์— ๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ ์‚ฌ์šฉ์„ ์œ„ํ•œ ์•„๋ž˜ ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•œ๋‹ค.

apply plugin: "kotlin-kapt" android { .... dataBinding { enabled = true } }

2.2. DataBinding์„ ์œ„ํ•œ ViewModel ๋งŒ๋“ค๊ธฐ

xml์— ์—ฐ๋™ํ•  ViewModel.kt ํŒŒ์ผ์„ ์•„๋ž˜์™€ ๊ฐ™์ด ์ƒ์„ฑํ•œ๋‹ค.

class ViewModel { val text = ObservableField("")
fun showText(view: View) {
    Toast.makeText(view.context, "${text.get()}", Toast.LENGTH_SHORT).show()
}

}

๋ทฐ๋ชจ๋ธ ํด๋ž˜์Šค์— ์ž‘์„ฑํ•ด๋‘” ํ•จ์ˆ˜๋Š” xml์—์„œ ํ˜ธ์ถœ ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค๋งŒ, ์ ‘๊ทผ ์ œํ•œ์ž๊ฐ€ private๋ฉด xml์—์„œ ํ•จ์ˆ˜๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์—†๋‹ค.

2.3. layout ํŒŒ์ผ ์ˆ˜์ •ํ•˜๊ธฐ

2.3.1. layout & variable ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•ด ViewModel ์—ฐ๊ฒฐํ•˜๊ธฐ

๋ฐ์ดํ„ฐ ๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•ด์„œ ๋ฃจํŠธ ํƒœ๊ทธ๋ฅผ layout์œผ๋กœ ๋ฐ”๊ฟ” ์ฃผ๊ณ  variable ํƒœ๊ทธ๋ฅผ ํ†ตํ•ด ๋ ˆ์ด์•„์›ƒ์— ์—ฐ๋™ํ•  ViewModel์„ ์ง€์ •ํ•ด์ค€๋‹ค.

2.3.2. ViewModel ์ฐธ๊ณ ํ•˜๊ธฐ

ViewModel์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผ ํ•  ๋•Œ์—๋Š”, @{} ์•ˆ์— ์ฐธ์กฐ ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. DataBinding์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ ˆ์ด์•„์›ƒ์—์„œ ๊ฐ„๋‹จํ•œ ์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์œ„ ์ฝ”๋“œ์˜ android:visiblity๊ฐ€ ๊ฐ„๋‹จํ•œ ์˜ˆ๋‹ค.

2.3.3. ๋ฆฌ์Šค๋„ˆ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ

DataBinding์—์„œ๋Š” android:onClick๋“ฑ์˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘์„ ๋ฐ”์ธ๋”ฉ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•จ์ˆ˜๋ฅผ ๋ฐ”์ธ๋”ฉ ํ•  ๋•Œ์—๋Š”, @{() -> vm.doSomething()} ๊ฐ™์ด ๋žŒ๋‹ค์‹์„ ์‚ฌ์šฉํ•ด์„œ ํ˜ธ์ถœํ•œ๋‹ค. ์ด ๋•Œ ํ•จ์ˆ˜๋Š” ์ด๋ฒคํŠธ์˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋ฐ›๊ฑฐ๋‚˜ context ๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•„์„œ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

2.4. Acvitity์—์„œ ViewModel ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ

๋ฐ”์ธ๋”ฉ์„ ์œ„ํ•ด Activity ํŒŒ์ผ์— ์•„๋ž˜์˜ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•œ๋‹ค. 

val binding = DataBindingUtil.setContentView(this, R.layout.activity_main) binding.vm = ViewModel()

setContentView๋Š” DataBinding์„ ํ•˜๋ฉฐ ์จ์ฃผ๊ธฐ ๋•Œ๋ฌธ์—, ์ž‘์„ฑ ํ•  ํ•„์š”๊ฐ€ ์—†์œผ๋‹ˆ ์ง€์›Œ์ค€๋‹ค. ๋ฐ์ดํ„ฐ๋ฐ”์ธ๋”ฉ ํด๋ž˜์Šค๋Š” ์€ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์˜ ์ด๋ฆ„์— ๋”ฐ๋ผ์„œ ์ž๋™์œผ๋กœ ์นด๋ฉœ ์ผ€์ด์Šค๋กœ ๋ณ€ํ™˜๋˜๊ณ , ๊ทธ ๋’ค์— Binding์„ ๋ถ™์—ฌ์„œ ์™„์„ฑ๋œ๋‹ค. ex) activity_main โ†’ ActivityMainBinding binding.vm ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ๋ ˆ์ด์•„์›ƒ์˜ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•œ๋‹ค. binding.vm = ViewModel() ๋กœ ๋ทฐ๋ชจ๋ธ์„ ๋ฐ”์ธ๋”ฉํ•ด์ค€๋‹ค.

3. ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌํ•˜๊ธฐ

๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ทฐ์—์„œ ์ „๋‹ฌ๋˜๋Š” ํ‘œํ˜„์‹ ์ฒ˜๋ฆฌ ์ด๋ฒคํŠธ๋ฅผ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค(์˜ˆ: onClick() ๋ฉ”์„œ๋“œ). ์ด๋ฒคํŠธ ์†์„ฑ ์ด๋ฆ„์€ ๋ช‡ ๊ฐ€์ง€ ์˜ˆ์™ธ๋ฅผ ์ œ์™ธํ•˜๊ณ  ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์˜ ์ด๋ฆ„์— ๋”ฐ๋ผ ๊ฒฐ์ •๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด View.OnClickListener์—๋Š” onClick() ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์œผ๋ฏ€๋กœ ์ด ์ด๋ฒคํŠธ์˜ ์†์„ฑ์€ android:onClick์ด๋‹ค. ํด๋ฆญ ์ด๋ฒคํŠธ์—๋Š” ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด android:onClick ์ด์™ธ์˜ ๋‹ค๋ฅธ ์†์„ฑ์ด ํ•„์š”ํ•œ ํŠน์ˆ˜ํ•œ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ์žˆ๋‹ค. ๊ฐœ๋ฐœ์ž๋Š” ์•„๋ž˜ 2๊ฐœ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Method reference: ํ‘œํ˜„์‹์—์„œ ๋ฆฌ์Šค๋„ˆ ๋ฉ”์„œ๋“œ์˜ ์„œ๋ช…๊ณผ ์ผ์น˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. ํ‘œํ˜„์‹์ด ๋ฉ”์„œ๋“œ ์ฐธ์กฐ๋กœ ๊ณ„์‚ฐ๋˜๋ฉด ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ๋ฆฌ์Šค๋„ˆ์—์„œ ๋ฉ”์„œ๋“œ ์ฐธ์กฐ ๋ฐ ์†Œ์œ ์ž ๊ฐ์ฒด๋ฅผ ๋ž˜ํ•‘ํ•˜๊ณ  ํƒ€๊ฒŸ ๋ทฐ์—์„œ ์ด ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•œ๋‹ค. ํ‘œํ˜„์‹์ด null๋กœ ๊ณ„์‚ฐ๋˜๋ฉด ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  null ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•œ๋‹ค.
  • Listener binding: ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๊ณ„์‚ฐ๋˜๋Š” ๋žŒ๋‹ค ํ‘œํ˜„์‹์ด๋‹ค. ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ํ•ญ์ƒ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋ทฐ์—์„œ ์„ค์ •ํ•œ๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ ์ „๋‹ฌ๋˜๋ฉด ๋ฆฌ์Šค๋„ˆ๋Š” ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ๊ณ„์‚ฐํ•œ๋‹ค.

3.1. Method reference

์ด๋ฒคํŠธ๋Š” android:onClick์ด ํ™œ๋™์˜ ๋ฉ”์„œ๋“œ์— ํ• ๋‹น๋˜๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ํ•ธ๋“ค๋Ÿฌ ๋ฉ”์„œ๋“œ์— ์ง์ ‘ ๊ฒฐํ•ฉ๋  ์ˆ˜ ์žˆ๋‹ค. View onClick ์†์„ฑ๊ณผ ๋น„๊ตํ–ˆ์„ ๋•Œ ์ฃผ์š” ์ด์ ์€ ํ‘œํ˜„์‹์ด ์ปดํŒŒ์ผ ํƒ€์ž„์— ์ฒ˜๋ฆฌ๋˜๋ฏ€๋กœ ๋ฉ”์„œ๋“œ๊ฐ€ ์—†๊ฑฐ๋‚˜ ์„œ๋ช…์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์œผ๋ฉด ์ปดํŒŒ์ผ ํƒ€์ž„ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. Method reference์™€  Listener binding์˜ ์ฃผ์š” ์ฐจ์ด์ ์€ ์‹ค์ œ ๋ฆฌ์Šค๋„ˆ ๊ตฌํ˜„์ด ์ด๋ฒคํŠธ๊ฐ€ ํŠธ๋ฆฌ๊ฑฐ๋  ๋•Œ๊ฐ€ ์•„๋‹ˆ๋ผ ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฒฐํ•ฉ๋  ๋•Œ ์ƒ์„ฑ๋œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ํ‘œํ˜„์‹์„ ๊ณ„์‚ฐํ•˜๋ ค๋ฉด Listener binding์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค. ํ•ธ๋“ค๋Ÿฌ์— ์ด๋ฒคํŠธ๋ฅผ ํ• ๋‹นํ•˜๋ ค๋ฉด ํ˜ธ์ถœํ•  ๋ฉ”์„œ๋“œ ์ด๋ฆ„์ด ๋  ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ฐ˜ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

class MyHandlers {         fun onClickFriend(view: View) { ... } }

// ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ทฐ์˜ ํด๋ฆญ ๋ฆฌ์Šค๋„ˆ๋ฅผ onClickFriend() ๋ฉ”์„œ๋“œ์— ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.

                                                   <LinearLayout            android:orientation="vertical"            android:layout_width="match_parent"            android:layout_height="match_parent">            <TextView android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="@{user.firstName}"                android:onClick="@{handlers::onClickFriend}"/>            

3.2. Listener binding

Listener binding์€ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์‹คํ–‰๋˜๋Š” ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์ด๋‹ค. Listener binding์€ Method reference์™€ ๋น„์Šทํ•˜๋‹ค. ํ•˜์ง€๋งŒ Listener binding์„ ์‚ฌ์šฉํ•˜๋ฉด ์ž„์˜์˜ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ Gradle ๋ฒ„์ „ 2.0 ์ด์ƒ์„ ์œ„ํ•œ Android Gradle ํ”Œ๋Ÿฌ๊ทธ์ธ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Methon reference์—์„œ ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ์ผ์น˜ํ•ด์•ผ ํ•œ๋‹ค. Listener binding์—์„œ๋Š” ๋ฐ˜ํ™˜ ๊ฐ’๋งŒ ๋ฆฌ์Šค๋„ˆ์˜ ์˜ˆ์ƒ ๋ฐ˜ํ™˜ ๊ฐ’๊ณผ ์ผ์น˜ํ•˜๋ฉด ๋œ๋‹ค.(void๊ฐ€ ์˜ˆ์ƒ๋˜์ง€ ์•Š๋Š” ํ•œ).

class Presenter {         fun onSaveClick(task: Task){} }

// ๋‹ค์Œ๊ณผ ๊ฐ™์ด ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ onSaveClick() ๋ฉ”์„œ๋“œ์— ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค.

                                                                <Button android:layout_width="wrap_content" android:layout_height="wrap_content"             android:onClick="@{() -> presenter.onSaveClick(task)}" />            

ํ‘œํ˜„์‹์— ์ฝœ๋ฐฑ์„ ์‚ฌ์šฉํ•˜๋ฉด Data Binding์€ ํ•„์š”ํ•œ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜์—ฌ ์ด๋ฒคํŠธ์— ๋“ฑ๋กํ•œ๋‹ค. ๋ทฐ์—์„œ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด Data Binding์€ ์ฃผ์–ด์ง„ ํ‘œํ˜„์‹์„ ๊ณ„์‚ฐํ•œ๋‹ค. ์ผ๋ฐ˜ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์—์„œ์™€ ๊ฐ™์ด ์ด๋Ÿฌํ•œ ๋ฆฌ์Šค๋„ˆ ํ‘œํ˜„์‹์ด ๊ณ„์‚ฐ๋˜๋Š” ๋™์•ˆ ๊ณ„์† Data Binding์˜ null ๋ฐ ์Šค๋ ˆ๋“œ ์•ˆ์ „์„ฑ์ด ํ™•๋ณด๋œ๋‹ค. ์œ„์˜ ์˜ˆ์—์„œ๋Š” onClick(View)์— ์ „๋‹ฌ๋˜๋Š” view ๋งค๊ฐœ๋ณ€์ˆ˜๊ฐ€ ์ •์˜๋˜์ง€ ์•Š์•˜๋‹ค. Listener binding์—์„œ๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹(๋ชจ๋“  ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฌด์‹œ, ๋ชจ๋“  ๋งค๊ฐœ๋ณ€์ˆ˜์˜ ์ด๋ฆ„์„ ์ง€์ •)์œผ๋กœ ๋ฆฌ์Šค๋„ˆ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„ ์ง€์ •์„ ์„ ํƒํ•˜๋ฉด ํ‘œํ˜„์‹์— ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์˜ ํ‘œํ˜„์‹์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

android:onClick="@{(view) -> presenter.onSaveClick(task)}"

class Presenter {         fun onSaveClick(view: View, task: Task){} }

android:onClick="@{(theView) -> presenter.onSaveClick(theView, task)}"

์•„๋ž˜์™€ ๊ฐ™์ด ๋‘˜ ์ด์ƒ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ํ•จ๊ป˜ ๋žŒ๋‹ค ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

class Presenter {         fun onCompletedChanged(task: Task, completed: Boolean){} }

<CheckBox android:layout_width="wrap_content" android:layout_height="wrap_content"           android:onCheckedChanged="@{(cb, isChecked) -> presenter.completeChanged(task, isChecked)}" />

์ˆ˜์‹  ์ค‘์ธ ์ด๋ฒคํŠธ๊ฐ€ void๊ฐ€ ์•„๋‹Œ ์œ ํ˜•์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ํ‘œํ˜„์‹๋„ ๊ฐ™์€ ์œ ํ˜•์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด '๊ธธ๊ฒŒ ํด๋ฆญ' ์ด๋ฒคํŠธ๋ฅผ ์ˆ˜์‹  ๋Œ€๊ธฐํ•˜๋ ค๋ฉด ํ‘œํ˜„์‹์—์„œ Boolean์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค.

class Presenter {         fun onLongClick(view: View, task: Task): Boolean { } }

android:onLongClick="@{(theView) -> presenter.onLongClick(theView, task)}"

null ๊ฐ์ฒด๋กœ ์ธํ•ด ํ‘œํ˜„์‹์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์—†์œผ๋ฉด ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ๊ฐ๊ธฐ ํ•ด๋‹นํ•˜๋Š” ์œ ํ˜•์˜ ๊ธฐ๋ณธ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฐธ์กฐ ์œ ํ˜•์€ null์„, int๋Š” 0์„, boolean์€ false๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์กฐ๊ฑด์ž์™€ ํ•จ๊ป˜ ํ‘œํ˜„์‹(์˜ˆ: ์‚ผํ•ญ)์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค๋ฉด void๋ฅผ ๊ธฐํ˜ธ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

android:onClick="@{(v) -> v.isVisible() ? doSomething() : void}"

3.3. Imports, variables, and includes

Data Binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Imports, variables, and includes๊ณผ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค. Imports๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๋‚ด์—์„œ ํด๋ž˜์Šค๋ฅผ ์‰ฝ๊ฒŒ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. variables๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค. includes์„ ์‚ฌ์šฉํ•˜๋ฉด ์•ฑ ์ „์ฒด์—์„œ ๋ณต์žกํ•œ ๋ ˆ์ด์•„์›ƒ์„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

3.3.1. Imports

Imports๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ด€๋ฆฌํ˜• ์ฝ”๋“œ์—์„œ์™€ ๊ฐ™์ด ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๋‚ด์—์„œ ํด๋ž˜์Šค๋ฅผ ์‰ฝ๊ฒŒ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. 0๊ฐœ ์ด์ƒ์˜ import ์š”์†Œ๋ฅผ data ์š”์†Œ ๋‚ด์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์˜ˆ๋Š” View ํด๋ž˜์Šค๋ฅผ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ๋กœ ๊ฐ€์ ธ์˜จ๋‹ค.

       

// View ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ ธ์˜ค๋ฉด ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์—์„œ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. <TextView        android:text="@{user.lastName}"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:visibility="@{user.isAdult ? View.VISIBLE : View.GONE}"/>

3.3.1.1. Type aliases

ํด๋ž˜์Šค ์ด๋ฆ„ ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด ํด๋ž˜์Šค ์ค‘ ํ•˜๋‚˜์˜ ์ด๋ฆ„์„ ๋ณ„์นญ์œผ๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์˜ˆ๋Š” com.example.real.estate ํŒจํ‚ค์ง€์˜ View ํด๋ž˜์Šค ์ด๋ฆ„์„ Vista๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.

์ด์ œ Vista๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ com.example.real.estate.View๋ฅผ ์ฐธ์กฐํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๋‚ด์—์„œ android.view.View๋ฅผ ์ฐธ์กฐํ•˜๋Š” ๋ฐ View๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

3.3.1.2. Import other classes

๊ฐ€์ ธ์˜จ ์œ ํ˜•์€ ๋ณ€์ˆ˜ ๋ฐ ํ‘œํ˜„์‹์—์„œ ์œ ํ˜• ์ฐธ์กฐ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์˜ˆ๋Š” ๋ณ€์ˆ˜์˜ ์œ ํ˜•์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” User ๋ฐ List๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.โ€จ                                    

์ฃผ์˜: Android ์ŠคํŠœ๋””์˜ค์—์„œ๋Š” ์•„์ง ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ๊ฐ€์ ธ์˜จ ๋ณ€์ˆ˜์˜ ์ž๋™ ์™„์„ฑ์ด IDE์—์„œ ์ž‘๋™ํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์•ฑ์€ ์—ฌ์ „ํžˆ ์ปดํŒŒ์ผ๋œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ณ€์ˆ˜ ์ •์˜์—์„œ ์ •๊ทœํ™”๋œ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜์—ฌ IDE ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋‹ค.

๋˜ํ•œ ๊ฐ€์ ธ์˜จ ์œ ํ˜•์„ ์‚ฌ์šฉํ•˜์—ฌ ํ‘œํ˜„์‹์˜ ์ผ๋ถ€๋ฅผ ๋ณ€ํ™˜ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์˜ˆ๋Š” connection ์†์„ฑ์„ User ์œ ํ˜•์œผ๋กœ ๋ณ€ํ™˜ํ•œ๋‹ค.

๋˜ํ•œ ํ‘œํ˜„์‹์—์„œ ์ •์  ํ•„๋“œ ๋ฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ์กฐํ•  ๋•Œ ๊ฐ€์ ธ์˜จ ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” MyStringUtils ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ ธ์™€์„œ capitalize ๋ฉ”์„œ๋“œ๋ฅผ ์ฐธ์กฐํ•œ๋‹ค.

                โ€ฆ

3.3.4. Variables

data ์š”์†Œ ๋‚ด์—์„œ ์—ฌ๋Ÿฌ variable ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐ variable ์š”์†Œ๋Š” ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๋‚ด ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์— ์‚ฌ์šฉ๋  ๋ ˆ์ด์•„์›ƒ์—์„œ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋Š” ์†์„ฑ์„ ์„ค๋ช…ํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ๋Š” user, image ๋ฐ note ๋ณ€์ˆ˜๋ฅผ ์„ ์–ธํ•œ๋‹ค.

                               

๋ณ€์ˆ˜ ์œ ํ˜•์€ ์ปดํŒŒ์ผ ํƒ€์ž„์— ๊ฒ€์‚ฌ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ๋ณ€์ˆ˜๊ฐ€ Observable์„ ๊ตฌํ˜„ํ•˜๊ฑฐ๋‚˜ ์‹๋ณ„ ๊ฐ€๋Šฅํ•œ ์ปฌ๋ ‰์…˜์ด๋ผ๋ฉด ์œ ํ˜•์— ๋ฐ˜์˜๋œ๋‹ค. ๋ณ€์ˆ˜๊ฐ€ Observable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋ณธ ํด๋ž˜์Šค ๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ผ๋ฉด ๋ณ€์ˆ˜๋“ค์ด ๊ด€์ฐฐ๋˜์ง€ ์•Š๋Š”๋‹ค. ๋‹ค์–‘ํ•œ ๊ตฌ์„ฑ(์˜ˆ: ๊ฐ€๋กœ ๋ชจ๋“œ ๋˜๋Š” ์„ธ๋กœ ๋ชจ๋“œ)์˜ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ์ด ์„œ๋กœ ๋‹ค๋ฅผ ๋•Œ ๋ณ€์ˆ˜๊ฐ€ ๊ฒฐํ•ฉ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ ๊ฐ„์— ์ถฉ๋Œํ•˜๋Š” ๋ณ€์ˆ˜ ์ •์˜๊ฐ€ ์žˆ์–ด์„œ๋Š” ์•ˆ๋œ๋‹ค. ์ƒ์„ฑ๋œ ๊ฒฐํ•ฉ ํด๋ž˜์Šค์—๋Š” ์„ค๋ช…๋œ ๊ฐ ๋ณ€์ˆ˜์˜ setter ๋ฐ getter๊ฐ€ ์žˆ๋‹ค. ๋ณ€์ˆ˜๋Š” setter๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ๊นŒ์ง€ ๊ธฐ๋ณธ ๊ด€๋ฆฌํ˜• ์ฝ”๋“œ ๊ฐ’์„ ์‚ฌ์šฉํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ฐธ์กฐ ์œ ํ˜•์€ null์„, int๋Š” 0์„, boolean์€ false๋ฅผ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. ํ•„์š”์— ๋”ฐ๋ผ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์— ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด context๋ผ๋Š” ์ด๋ฆ„์˜ ํŠน์ˆ˜ ๋ณ€์ˆ˜๋ฅผ ์ƒ์„ฑํ•œ๋‹ค.  context์˜ ๊ฐ’์€ ๋ฃจํŠธ ๋ทฐ์˜ getContext() ๋ฉ”์„œ๋“œ์—์„œ ์˜จ Context ๊ฐ์ฒด์ด๋‹ค.  context ๋ณ€์ˆ˜๊ฐ€ ์ด ์ด๋ฆ„์„ ์‚ฌ์šฉํ•˜๋Š” ๋ช…์‹œ์  ๋ณ€์ˆ˜ ์„ ์–ธ์œผ๋กœ ์žฌ์ •์˜๋œ๋‹ค.

3.3.5. Includes

์†์„ฑ์— ์•ฑ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ๋ฐ ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ํฌํ•จํ•˜๋Š” ๋ ˆ์ด์•„์›ƒ์—์„œ ํฌํ•จ๋œ ๋ ˆ์ด์•„์›ƒ์˜ ๊ฒฐํ•ฉ์œผ๋กœ ๋ณ€์ˆ˜๋ฅผ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์Œ ์˜ˆ๋Š” name.xml ๋ฐ contact.xml ๋ ˆ์ด์•„์›ƒ ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ํฌํ•จ๋œ user ๋ณ€์ˆ˜๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

                                                                       

Data binding์€ Includes๋ฅผ ๋ณ‘ํ•ฉ ์š”์†Œ์˜ ์ง์ ‘ ํ•˜์œ„ ์š”์†Œ๋กœ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์•„๋ž˜ ๋ ˆ์ด์•„์›ƒ์€ ์ง€์›๋˜์ง€ ์•Š๋Š”๋‹ค.

                                                                       

4. Binding adapters

Binding adapter๋Š” ์ ์ ˆํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ์ž‘์—…์„ ๋‹ด๋‹นํ•œ๋‹ค. ํ•œ ๊ฐ€์ง€ ์˜ˆ๋กœ setText() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์†์„ฑ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ์ž‘์—…์„ ๋“ค ์ˆ˜ ์žˆ๋‹ค. ๋˜ ๋‹ค๋ฅธ ์˜ˆ๋กœ๋Š” setOnClickListener() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์ด ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•˜๋Š” ์ž‘์—…์ด ์žˆ๋‹ค. Data Binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ’์„ ์„ค์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ˜ธ์ถœ๋˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜๊ณ  ๊ณ ์œ ํ•œ Binding ๋กœ์ง์„ ์ œ๊ณตํ•˜๋ฉฐ ์–ด๋Œ‘ํ„ฐ๋ฅผ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด์˜ ์œ ํ˜•์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

4.1. Setting attribute values

๊ฒฐํ•ฉ๋œ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค ์ƒ์„ฑ๋œ ๊ฒฐํ•ฉ ํด๋ž˜์Šค๋Š” ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ทฐ์—์„œ setter ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•œ๋‹ค. Data Binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ๊ฒฐ์ •ํ•˜๊ฑฐ๋‚˜ ๋ฉ”์„œ๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ ์–ธํ•˜๊ฑฐ๋‚˜ ๋งž์ถค ๋กœ์ง์„ ์ œ๊ณตํ•ด ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•˜๋„๋ก ํ—ˆ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

4.1.1. Automatic method selection

์ด๋ฆ„์ด example์ธ ์†์„ฑ์˜ ๊ฒฝ์šฐ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ํ˜ธํ™˜ ๊ฐ€๋Šฅํ•œ ์œ ํ˜•์„ ์ธ์ˆ˜๋กœ ํ—ˆ์šฉํ•˜๋Š” setExample(arg) ๋ฉ”์„œ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ์œผ๋ ค๊ณ  ํ•œ๋‹ค. ์†์„ฑ์˜ ๋„ค์ž„์ŠคํŽ˜์ด์Šค๋Š” ๊ณ ๋ ค๋˜์ง€ ์•Š์œผ๋ฉฐ ๋ฉ”์„œ๋“œ ๊ฒ€์ƒ‰ ์‹œ ์†์„ฑ ์ด๋ฆ„ ๋ฐ ์œ ํ˜•๋งŒ ์‚ฌ์šฉ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด android:text="@{user.name}" ํ‘œํ˜„์‹์ด ์žˆ๋‹ค๊ณ  ํ•œ๋‹ค๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” user.getName()์—์„œ ๋ฐ˜ํ™˜ํ•œ ์œ ํ˜•์„ ํ—ˆ์šฉํ•˜๋Š” setText(arg) ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ๋Š”๋‹ค.  user.getName()์˜ ๋ฐ˜ํ™˜ ์œ ํ˜•์ด String์ด๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” String ์ธ์ˆ˜๋ฅผ ํ—ˆ์šฉํ•˜๋Š” setText() ๋ฉ”์„œ๋“œ๋ฅผ ์ฐพ๋Š”๋‹ค. ํ‘œํ˜„์‹์ด int๋ฅผ ๋Œ€์‹  ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” int ์ธ์ˆ˜๋ฅผ ํ—ˆ์šฉํ•˜๋Š” setText() ๋ฉ”์„œ๋“œ๋ฅผ ๊ฒ€์ƒ‰ํ•œ๋‹ค. ํ‘œํ˜„์‹์€ ์˜ฌ๋ฐ”๋ฅธ ์œ ํ˜•์„ ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. ํ•„์š”ํ•˜๋‹ค๋ฉด ๋ฐ˜ํ™˜ ๊ฐ’์„ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ง€์ •๋œ ์ด๋ฆ„์˜ ์†์„ฑ์ด ์—†๋”๋ผ๋„ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ์ž‘๋™ํ•œ๋‹ค. ๊ทธ๋•Œ๋Š” ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ setter์— ํ•„์š”ํ•œ ์†์„ฑ์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ง€์› ํด๋ž˜์Šค DrawerLayout์—๋Š” ์–ด๋–ค ์†์„ฑ๋„ ์—†์ง€๋งŒ ๋งŽ์€ setter๊ฐ€ ์žˆ๋‹ค. ์•„๋ž˜ ๋ ˆ์ด์•„์›ƒ์€ ์ž๋™์œผ๋กœ setScrimColor(int) ๋ฐ setDrawerListener(DrawerListener) ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ๊ฐapp:scrimColor ๋ฐ app:drawerListener ์†์„ฑ์˜ setter๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

4.1.2. Specify a custom method name

์ผ๋ถ€ ์†์„ฑ์—๋Š” ์ด๋ฆ„์ด ์ผ์น˜ํ•˜์ง€ ์•Š๋Š” setter๊ฐ€ ์žˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ƒํ™ฉ์—์„œ ์†์„ฑ์€ BindingMethods ์ฃผ์„์„ ์‚ฌ์šฉํ•˜์—ฌ setter์™€ ์—ฐ๊ฒฐ๋  ์ˆ˜ ์žˆ๋‹ค. ์ฃผ์„์€ ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜๋ฉฐ ์ด๋ฆ„์ด ๋ฐ”๋€ ๊ฐ ๋ฉ”์„œ๋“œ์— ํ•˜๋‚˜์”ฉ ์—ฌ๋Ÿฌ BindingMethod ์ฃผ์„์„ ํฌํ•จํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐํ•ฉ ๋ฉ”์„œ๋“œ๋Š” ์•ฑ์˜ ์–ด๋–ค ํด๋ž˜์Šค์—๋„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์„์ด๋‹ค. ์•„๋ž˜ ์˜ˆ์—์„œ android:tint ์†์„ฑ์€ setTint() ๋ฉ”์„œ๋“œ๊ฐ€ ์•„๋‹Œ setImageTintList(ColorStateList) ๋ฉ”์„œ๋“œ์™€ ์—ฐ๊ฒฐ๋œ๋‹ค.

@BindingMethods(value = [         BindingMethod(             type = android.widget.ImageView::class,             attribute = "android:tint",             method = "setImageTintList")])

์ผ๋ฐ˜์ ์œผ๋กœ Android ํ”„๋ ˆ์ž„์›Œํฌ ํด๋ž˜์Šค์—์„œ setter์˜ ์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ํ•„์š”๊ฐ€ ์—†๋‹ค. ์ด๋ฆ„ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜์—ฌ ์ผ์น˜ํ•˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ๋Š” ์†์„ฑ์ด ์ด๋ฏธ ๊ตฌํ˜„๋˜์–ด์žˆ๋‹ค.

4.1.3. Provide custom logic

์ผ๋ถ€ ์†์„ฑ์—๋Š” ๋งž์ถค ๊ฒฐํ•ฉ ๋กœ์ง์ด ํ•„์š”ํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด android:paddingLeft ์†์„ฑ์—๋Š” ์—ฐ๊ฒฐ๋œ setter๊ฐ€ ์—†๋Š” ๋Œ€์‹   setPadding(left, top, right, bottom) ๋ฉ”์„œ๋“œ๊ฐ€ ์ œ๊ณต๋œ๋‹ค.  BindingAdapter ์ฃผ์„์ด ์žˆ๋Š” ์ •์  ๊ฒฐํ•ฉ ์–ด๋Œ‘ํ„ฐ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์†์„ฑ์˜ setter๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ๋ฐฉ์‹์„ ๋งž์ถค ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. Android ํ”„๋ ˆ์ž„์›Œํฌ ํด๋ž˜์Šค์˜ ์†์„ฑ์—๋Š” BindingAdapter ์ฃผ์„์ด ์ด๋ฏธ ์ƒ์„ฑ๋˜์–ด ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ๋Š” paddingLeft ์†์„ฑ์˜ ๊ฒฐํ•ฉ ์–ด๋Œ‘ํ„ฐ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, padding: Int) {         view.setPadding(padding,                     view.getPaddingTop(),                     view.getPaddingRight(),                     view.getPaddingBottom()) }

๋งค๊ฐœ๋ณ€์ˆ˜ ์œ ํ˜•์€ ์ค‘์š”ํ•˜๋‹ค. ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์†์„ฑ๊ณผ ์—ฐ๊ฒฐ๋œ ๋ทฐ์˜ ์œ ํ˜•์„ ๊ฒฐ์ •ํ•œ๋‹ค. ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์ง€์ •๋œ ์†์„ฑ์˜ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์—์„œ ํ—ˆ์šฉ๋˜๋Š” ์œ ํ˜•์„ ๊ฒฐ์ •ํ•œ๋‹ค. BindingAdapter๋Š” ๋‹ค๋ฅธ ์œ ํ˜•์˜ ๋งž์ถค์„ค์ •์— ์œ ์šฉํ•˜๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋งž์ถค ๋กœ๋”๋Š” ์ž‘์—…์ž ์Šค๋ ˆ๋“œ์—์„œ ํ˜ธ์ถœ๋˜์–ด ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ •์˜ํ•˜๋Š” BindingAdapter๋Š” ์ถฉ๋Œ์ด ๋ฐœ์ƒํ•˜๋ฉด Android ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋ณธ ์–ด๋Œ‘ํ„ฐ๋ณด๋‹ค ์šฐ์„  ์ ์šฉ๋œ๋‹ค. ๋˜ํ•œ ์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์—ฌ๋Ÿฌ ์†์„ฑ์„ ๋ฐ›๋Š” ์–ด๋Œ‘ํ„ฐ๋„ ์žˆ์„ ์ˆ˜ ์žˆ๋‹ค.

@BindingAdapter("imageUrl", "error") fun loadImage(view: ImageView, url: String, error: Drawable) {         Picasso.get().load(url).error(error).into(view) }

์œ„ BindingAdapter๋Š” ๋ ˆ์ด์•„์›ƒ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๊ธฐ์„œ @drawable/venueError๋Š” ์•ฑ์˜ ๋ฆฌ์†Œ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค. ๋ฆฌ์†Œ์Šค๋ฅผ @{}๋กœ ๋ฌถ์œผ๋ฉด ์œ ํšจํ•œ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์ด ๋œ๋‹ค.

imageUrl๊ณผ error๊ฐ€ ๋ชจ๋‘ ImageView ๊ฐ์ฒด์— ์‚ฌ์šฉ๋˜๋Š”๋ฐ imageUrl์€ ๋ฌธ์ž์—ด์ด๊ณ  error๋Š” Drawable์ด๋ผ๋ฉด ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ˜ธ์ถœ๋œ๋‹ค.  ์–ด๋–ค ์†์„ฑ์ด๋ผ๋„ ์„ค์ •๋  ๋•Œ ์–ด๋Œ‘ํ„ฐ๋ฅผ ํ˜ธ์ถœํ•˜๋ ค๋ฉด ๋‹ค์Œ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์–ด๋Œ‘ํ„ฐ์˜ requireAll ํ”Œ๋ž˜๊ทธ(์„ ํƒ์‚ฌํ•ญ)๋ฅผ  false๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

@BindingAdapter(value = ["imageUrl", "placeholder"], requireAll = false) fun setImageUrl(imageView: ImageView, url: String?, placeHolder: Drawable?) {         if (url == null) {             imageView.setImageDrawable(placeholder);         } else {             MyImageLoader.loadInto(imageView, url, placeholder);         } }

BindingAdapter ๋ฉ”์„œ๋“œ๋Š” ์„ ํƒ์ ์œผ๋กœ ํ•ธ๋“ค๋Ÿฌ์˜ ์ด์ „ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด์ „ ๊ฐ’๊ณผ ์ƒˆ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”์„œ๋“œ๋Š” ์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์†์„ฑ์˜ ๋ชจ๋“  ์ด์ „ ๊ฐ’์„ ๋จผ์ € ์„ ์–ธํ•œ ํ›„ ์ƒˆ ๊ฐ’์„ ์„ ์–ธํ•ด์•ผ ํ•œ๋‹ค.

@BindingAdapter("android:paddingLeft") fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {         if (oldPadding != newPadding) {             view.setPadding(padding,                         view.getPaddingTop(),                         view.getPaddingRight(),                         view.getPaddingBottom())         }  }

์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋Š” ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ํ•˜๋‚˜์˜ ์ถ”์ƒ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๋˜๋Š” ์ถ”์ƒ ํด๋ž˜์Šค์—์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ฆฌ์Šค๋„ˆ์— ์—ฌ๋Ÿฌ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ์—ฌ๋Ÿฌ ๋ฆฌ์Šค๋„ˆ๋กœ ๋ถ„ํ• ํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด View.OnAttachStateChangeListener์—๋Š” onViewAttachedtoWindow(View) ๋ฐ onViewDetachedFromWindow(View) 2๊ฐœ ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ๋‹ค. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” 2๊ฐœ์˜ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜์—ฌ ์ด๋Ÿฌํ•œ ๋ฉ”์„œ๋“œ์˜ ์†์„ฑ ๋ฐ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ตฌ๋ณ„ํ•œ๋‹ค.

// Translation from provided interfaces in Java: @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewDetachedFromWindow {         fun onViewDetachedFromWindow(v: View) }

@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1) interface OnViewAttachedToWindow {         fun onViewAttachedToWindow(v: View) }

ํ•˜๋‚˜์˜ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ๋‹ค๋ฅธ ๋ฆฌ์Šค๋„ˆ์—๋„ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์–ด๋А ํ•œ ์†์„ฑ ๋˜๋Š” ๋‘˜ ๋‹ค์—์„œ ์ž‘๋™ํ•˜๋Š” ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ•„์š”ํ•˜๋‹ค. ์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ์ฃผ์„์—์„œ requireAll์„ false๋กœ ์„ค์ •ํ•˜์—ฌ ๋ชจ๋“  ์†์„ฑ์— ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์„ ํ• ๋‹นํ•  ํ•„์š”๋Š” ์—†๋‹ค๋Š” ๊ฒƒ์„ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

@BindingAdapter(             "android:onViewDetachedFromWindow",             "android:onViewAttachedToWindow",             requireAll = false ) fun setListener(view: View, detach: OnViewDetachedFromWindow?, attach: OnViewAttachedToWindow?) {         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1) {             val newListener: View.OnAttachStateChangeListener?             newListener = if (detach == null && attach == null) {                 null             } else {                 object : View.OnAttachStateChangeListener {                     override fun onViewAttachedToWindow(v: View) {                         attach.onViewAttachedToWindow(v)                     }

                    override fun onViewDetachedFromWindow(v: View) {                         detach.onViewDetachedFromWindow(v)                     }                 }             }

            val oldListener: View.OnAttachStateChangeListener? =                     ListenerUtil.trackListener(view, newListener, R.id.onAttachStateChangeListener)             if (oldListener != null) {                 view.removeOnAttachStateChangeListener(oldListener)             }             if (newListener != null) {                 view.addOnAttachStateChangeListener(newListener)             }         } }

4.2. Object conversions

4.2.1. Automatic object conversion

Object๊ฐ€ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์—์„œ ๋ฐ˜ํ™˜๋˜๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์†์„ฑ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ์„ ํƒํ•œ๋‹ค.  Object๋Š” ์„ ํƒ๋œ ๋ฉ”์„œ๋“œ์˜ ๋งค๊ฐœ๋ณ€์ˆ˜ ์œ ํ˜•์œผ๋กœ ๋ณ€ํ™˜๋œ๋‹ค. ์ด ๋™์ž‘์€ ์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ObservableMapํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ์•ฑ์—์„œ ์œ ์šฉํ•˜๋‹ค.

์ฐธ๊ณ : object.key ํ‘œ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ๋งต์—์„œ ๊ฐ’์„ ์ฐธ์กฐํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์˜ ์˜ˆ์—์„œ @{userMap["lastName"]}์„ @{userMap.lastName}์œผ๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.

4.2.2. Custom conversions

์–ด๋–ค ์ƒํ™ฉ์—์„œ๋Š” ํŠน์ • ์œ ํ˜• ๊ฐ„์— ๋งž์ถค ๋ณ€ํ™˜์ด ํ•„์š”ํ•˜๋‹ค. ๋ทฐ์˜ android:background ์†์„ฑ์— Drawable์ด ํ•„์š”ํ•œ๋ฐ ์ง€์ •๋œ color ๊ฐ’์ด ์ •์ˆ˜์ธ ์ƒํ™ฉ์„ ์˜ˆ๋กœ ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์˜ˆ๋Š” Drawable์ด ํ•„์š”ํ•œ๋ฐ ์ •์ˆ˜๊ฐ€ ๋Œ€์‹  ์ง€์ •๋œ ์†์„ฑ์„ ๋ณด์—ฌ์ค€๋‹ค.

Drawable์ด ํ•„์š”ํ•œ๋ฐ ์ •์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋  ๋•Œ๋งˆ๋‹ค int๊ฐ€ ColorDrawble๋กœ ๋ณ€ํ™˜๋˜์–ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด BindingConversion ์ฃผ์„์ด ์žˆ๋Š” ์ •์  ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ€ํ™˜์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

@BindingConversion fun convertColorToDrawable(color: Int) = ColorDrawable(color)

๋‹จ, ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์— ์ง€์ •ํ•˜๋Š” ๊ฐ’ ์œ ํ˜•์€ ์ผ๊ด€๋˜์–ด์•ผ ํ•˜๋ฏ€๋กœ ์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ๋™์ผํ•œ ํ‘œํ˜„์‹์— ์„œ๋กœ ๋‹ค๋ฅธ ์œ ํ˜•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค.

5. Bind layout views to Architecture Components

AndroidX ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์—๋Š” ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚˜๊ณ  ํ…Œ์ŠคํŠธ์™€ ์œ ์ง€๊ด€๋ฆฌ๊ฐ€ ์‰ฌ์šด ์•ฑ์„ ๋””์ž์ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” Architecture Components๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ๋‹ค. Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” Architecture Components์™€ ์›ํ™œํ•˜๊ฒŒ ์—ฐ๋™ํ•˜์—ฌ UI ๊ฐœ๋ฐœ์„ ๋”์šฑ ๋‹จ์ˆœํ™”ํ•œ๋‹ค. ์•ฑ์˜ ๋ ˆ์ด์•„์›ƒ์€ ์ด๋ฏธ UI ์ปจํŠธ๋กค๋Ÿฌ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ  ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์„ ์•Œ๋ฆฌ๋„๋ก ๋•๋Š” ์•„ํ‚คํ…์ฒ˜ ๊ตฌ์„ฑ์š”์†Œ์˜ ๋ฐ์ดํ„ฐ์— ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค. ํ•ด๋‹น ํ•ญ๋ชฉ์—์„œ๋Š” ์•ฑ์— Architecture Components๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ์˜ ์ด์ ์„ ๋” ๊ฐ•ํ™”ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์„ค๋ช…ํ•œ๋‹ค.

5.1. Use LivaData to notify the UI about data change

LiveData ๊ฐ์ฒด๋ฅผ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ์†Œ์Šค๋กœ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ UI์— ์ž๋™์œผ๋กœ ์•Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. Observable fields์™€ ๊ฐ™์ด Observable์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฐ์ฒด์™€ ๋‹ฌ๋ฆฌ LiveData ๊ฐ์ฒด๋Š” ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ๊ตฌ๋…ํ•˜๋Š” ๊ด€์ฐฐ์ž์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค. ์ด ์ˆ˜๋ช… ์ฃผ๊ธฐ๋ฅผ ์•Œ๋ฉด LivaData์˜ ์ด์ ์„ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. Android ์ŠคํŠœ๋””์˜ค ๋ฒ„์ „ 3.1 ์ด์ƒ์—์„œ๋Š” ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ์ฝ”๋“œ์—์„œ Observable fields๋ฅผ LiveData ๊ฐ์ฒด๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋‹ค. ๊ฒฐํ•ฉ ํด๋ž˜์Šค์™€ ํ•จ๊ป˜ LiveData ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ˆ˜๋ช… ์ฃผ๊ธฐ ์†Œ์œ ์ž๋ฅผ ์ง€์ •ํ•˜์—ฌ LiveData ๊ฐ์ฒด์˜ ๋ฒ”์œ„๋ฅผ ์ •์˜ํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ์—์„œ๋Š” ๊ฒฐํ•ฉ ํด๋ž˜์Šค๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•œ ํ›„ ํ™œ๋™์„ ์ˆ˜๋ช… ์ฃผ๊ธฐ ์†Œ์œ ์ž๋กœ ์ง€์ •ํ•œ๋‹ค.

class ViewModelActivity : AppCompatActivity() {         override fun onCreate(savedInstanceState: Bundle?) {             // Inflate view and obtain an instance of the binding class.             val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

            // Specify the current activity as the lifecycle owner.             binding.setLifecycleOwner(this)         } }

ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ ˆ์ด์•„์›ƒ์— ๊ฒฐํ•ฉํ•  ์ˆ˜ ์žˆ๊ณ  LiveData ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ์—ฌ๋Ÿฌ ๋ฐ์ดํ„ฐ ์†Œ์Šค๋ฅผ ๋ณ‘ํ•ฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์˜ˆ๋Š” ViewModel์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค€๋‹ค.

class ScheduleViewModel : ViewModel() {         val userName: LiveData

        init {             val result = Repository.userName             userName = Transformations.map(result) { result -> result.value }         } }

5.2. Use ViewModel to manage UI-related data

Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ViewModel ๊ตฌ์„ฑ์š”์†Œ์™€ ์›ํ™œํ•˜๊ฒŒ ์—ฐ๋™ํ•œ๋‹ค. ์ด ๊ตฌ์„ฑ์š”์†Œ๋Š” ๋ ˆ์ด์•„์›ƒ์ด ๊ด€์ฐฐํ•˜๊ณ  ๋ณ€๊ฒฝ์‚ฌํ•ญ์— ๋ฐ˜์‘ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋…ธ์ถœํ•œ๋‹ค. Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด UI ๋กœ์ง์„ ๋ ˆ์ด์•„์›ƒ์—์„œ ๊ตฌ์„ฑ์š”์†Œ๋กœ ์‰ฝ๊ฒŒ ์ด๋™ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ๋” ์‰ฝ๋‹ค. Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ•„์š”ํ•  ๋•Œ ๋ทฐ๋ฅผ ๋ฐ์ดํ„ฐ ์†Œ์Šค์— ๊ฒฐํ•ฉํ•˜๊ณ  ๋ฐ์ดํ„ฐ ์†Œ์Šค์—์„œ ๊ฒฐํ•ฉ ํ•ด์ œํ•  ์ˆ˜ ์žˆ๋‹ค. Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ทธ ๋ฐ–์˜ ์ž‘์—…์€ ๋Œ€๋ถ€๋ถ„ ์ ์ ˆํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋…ธ์ถœํ•˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ์ด๋‹ค. Data binding ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ViewModel ํด๋ž˜์Šค์—์„œ ์ƒ์†๋ฐ›๋Š” ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•˜๊ณ  ๊ฒฐํ•ฉ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์™€ ๊ฒฐํ•ฉ ํด๋ž˜์Šค์˜ ์†์„ฑ์— ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ํ• ๋‹นํ•ด์•ผ ํ•œ๋‹ค. ์•„๋ž˜ ์˜ˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€ ํ•จ๊ป˜ ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค€๋‹ค.

class ViewModelActivity : AppCompatActivity() {         override fun onCreate(savedInstanceState: Bundle?) {             // Obtain the ViewModel component.             UserModel userModel = ViewModelProviders.of(getActivity())                                                       .get(UserModel.class)

            // Inflate view and obtain an instance of the binding class.             val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)

            // Assign the component to a property in the binding class.             binding.viewmodel = userModel         } }

์•„๋ž˜ ์˜ˆ์—์„œ์™€ ๊ฐ™์ด ๋ ˆ์ด์•„์›ƒ์—์„œ ๊ฒฐํ•ฉ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉํ•˜์—ฌ ์ ์ ˆํ•œ ๋ทฐ์— ViewModel ๊ตฌ์„ฑ์š”์†Œ์˜ ์†์„ฑ ๋ฐ ๋ฉ”์„œ๋“œ๋ฅผ ํ• ๋‹นํ•œ๋‹ค.

5.3. Use an Observable ViewModel for more control over binding adapters

Observable์„ ๊ตฌํ˜„ํ•˜๋Š” ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด LiveData ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์„ ๋‹ค๋ฅธ ์•ฑ ๊ตฌ์„ฑ์š”์†Œ์— ์•Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. LiveData์˜ ์ˆ˜๋ช… ์ฃผ๊ธฐ ๊ด€๋ฆฌ ๊ธฐ๋Šฅ์ด ์†์‹ค๋˜์—ˆ๋”๋ผ๋„ LiveData ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๋ณด๋‹ค Observable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ์ข‹์€ ์ƒํ™ฉ๋„ ์žˆ๋‹ค. Observable์„ ๊ตฌํ˜„ํ•˜๋Š” ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ฑ์˜ ๊ฒฐํ•ฉ ์–ด๋Œ‘ํ„ฐ๋ฅผ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ด ํŒจํ„ด์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ์•Œ๋ฆผ์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋งž์ถค ๋ฉ”์„œ๋“œ๋ฅผ ์ง€์ •ํ•˜์—ฌ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์˜ ์†์„ฑ ๊ฐ’์„ ์„ค์ •ํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ViewModel ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ตฌํ˜„ํ•˜๋ ค๋ฉด ViewModel ํด๋ž˜์Šค์—์„œ ์ƒ์†๋ฐ›๊ณ  Observable ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•œ๋‹ค. ๊ด€์ฐฐ์ž๊ฐ€ addOnPropertyChangedCallback() ๋ฐ removeOnPropertyChangedCallback() ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์•Œ๋ฆผ์„ ๊ตฌ๋…ํ•˜๊ฑฐ๋‚˜ ๊ตฌ๋… ์ทจ์†Œํ•  ๋•Œ ๋งž์ถค ๋กœ์ง์„ ์ œ๊ณตํ•  ์ˆ˜ ์žˆ๋‹ค. ๋˜ํ•œ notifyPropertyChanged() ๋ฉ”์„œ๋“œ์—์„œ ์†์„ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์‹คํ–‰๋˜๋Š” ๋งž์ถค ๋กœ์ง์„ ์ œ๊ณตํ•  ์ˆ˜๋„ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์˜ˆ๋Š” ๊ด€์ฐฐ ๊ฐ€๋Šฅํ•œ ViewModel์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค€๋‹ค.

/** * A ViewModel that is also an Observable, * to be used with the Data Binding Library. */ open class ObservableViewModel : ViewModel(), Observable {         private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry()

        override fun addOnPropertyChangedCallback(                 callback: Observable.OnPropertyChangedCallback) {             callbacks.add(callback)         }

        override fun removeOnPropertyChangedCallback(                 callback: Observable.OnPropertyChangedCallback) {             callbacks.remove(callback)         }

        /**          * Notifies observers that all properties of this instance have changed.          */         fun notifyChange() {             callbacks.notifyCallbacks(this, 0, null)         }

        /**          * Notifies observers that a specific property has changed. The getter for the          * property that changes should be marked with the @Bindable annotation to          * generate a field in the BR class to be used as the fieldId parameter.          *          * @param fieldId The generated BR id for the Bindable field.          */         fun notifyPropertyChanged(fieldId: Int) {             callbacks.notifyCallbacks(this, fieldId, null)         } }

6. Two-way data binding

์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ์ด ํ”„๋กœ์„ธ์Šค์˜ ๋ฐ”๋กœ๊ฐ€๊ธฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. '=' ๊ธฐํ˜ธ๊ฐ€ ํฌํ•จ๋œ @={} ํ‘œ๊ธฐ๋ฒ•์€ ์†์„ฑ๊ณผ ๊ด€๋ จ๋œ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ฐ›๋Š” ๋™์‹œ์— ์‚ฌ์šฉ์ž ์—…๋ฐ์ดํŠธ๋ฅผ ์ˆ˜์‹ ํ•œ๋‹ค.

โ€จ ๋ฐฑ์—… ๋ฐ์ดํ„ฐ์˜ ๋ณ€๊ฒฝ์— ๋Œ€์‘ํ•˜๊ธฐ ์œ„ํ•ด ๋‹ค์Œ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์—์„œ์™€ ๊ฐ™์ด ๋ ˆ์ด์•„์›ƒ ๋ณ€์ˆ˜๋ฅผ Observable ์ผ๋ฐ˜์ ์œผ๋กœ  BaseObservable์˜ ๊ตฌํ˜„์œผ๋กœ ๋งŒ๋“ค๊ณ  @Bindable ์ฃผ์„์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. class LoginViewModel : BaseObservable {         // val data = ...

        @Bindable         fun getRememberMe(): Boolean {             return data.rememberMe         }

        fun setRememberMe(value: Boolean) {             // Avoids infinite loops.             if (data.rememberMe != value) {                 data.rememberMe = value

                // React to the change.                 saveData()

                // Notify observers of a new value.                 notifyPropertyChanged(BR.remember_me)             }         } }

๊ฒฐํ•ฉ ๊ฐ€๋Šฅํ•œ ์†์„ฑ์˜ getter ๋ฉ”์„œ๋“œ๋Š” getRememberMe()๋ผ๊ณ  ํ•˜๋ฏ€๋กœ ์†์„ฑ์˜ ์ƒ์‘ํ•˜๋Š” setter ๋ฉ”์„œ๋“œ๋Š” ์ž๋™์œผ๋กœ setRememberMe()๋ผ๋Š” ์ด๋ฆ„์„ ์‚ฌ์šฉํ•œ๋‹ค.

6.1. Two-way data binding using custom attributes

ํ”Œ๋žซํผ์€ ์•ฑ์˜ ์ผ๋ถ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐ€์žฅ ์ผ๋ฐ˜์ ์ธ ์–‘๋ฐฉํ–ฅ ์†์„ฑ ๋ฐ ๋ณ€๊ฒฝ ๋ฆฌ์Šค๋„ˆ์˜ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ๊ตฌํ˜„์„ ์ œ๊ณตํ•œ๋‹ค. ๋งž์ถค ์†์„ฑ์œผ๋กœ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด @InverseBindingAdapter ๋ฐ @InverseBindingMethod๋ฅผ ํ™œ์šฉํ•ด์•ผ ํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด MyView๋ผ๋Š” ๋งž์ถค ๋ทฐ์—์„œ "time" ์†์„ฑ์— ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜ ๋‹จ๊ณ„๋ฅผ ์™„๋ฃŒํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

[Step 1] ์ดˆ๊ธฐ ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @BindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

โ€จ@BindingAdapter("time") @JvmStatic fun setTime(view: MyView, newValue: Time) {         // Important to break potential infinite loops.         if (view.time != newValue) {             view.time = newValue         } }

[Step 2] ๋ทฐ์—์„œ ๊ฐ’์„ ์ฝ๋Š” ๋ฉ”์„œ๋“œ์— @InverseBindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

@InverseBindingAdapter("time") @JvmStatic fun getTime(view: MyView) : Time {         return view.getTime() }

์ด ์‹œ์ ์—์„œ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ ํ•ด์•ผ ํ•  ์ž‘์—…(@BindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์„ ์ถ”๊ฐ€ํ•œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ)๊ณผ ๋ทฐ ์†์„ฑ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ํ˜ธ์ถœํ•  ๋Œ€์ƒ(InverseBindingListener ํ˜ธ์ถœ)์„ ์•Œ๊ณ  ์žˆ์ง€๋งŒ ์†์„ฑ์ด ์–ธ์ œ ์–ด๋–ป๊ฒŒ ๋ณ€๊ฒฝ๋˜๋Š”์ง€๋Š” ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์†์„ฑ์˜ ๋ณ€๊ฒฝ ์‹œ๊ธฐ ๋˜๋Š” ๋ฐฉ์‹์„ ์•Œ๊ธฐ ์œ„ํ•ด์„œ๋Š” ๋ทฐ์— ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•ด์•ผ ํ•œ๋‹ค. ๋ฆฌ์Šค๋„ˆ๋Š” ๋งž์ถค ๋ทฐ์™€ ์—ฐ๊ฒฐ๋œ ๋งž์ถค ๋ฆฌ์Šค๋„ˆ์ด๊ฑฐ๋‚˜ ํฌ์ปค์Šค ์ƒ์‹ค ๋˜๋Š” ํ…์ŠคํŠธ ๋ณ€๊ฒฝ๊ณผ ๊ฐ™์€ ์ œ๋„ค๋ฆญ ์ด๋ฒคํŠธ์ผ ์ˆ˜ ์žˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์†์„ฑ ๋ณ€๊ฒฝ์˜ ๋ฆฌ์Šค๋„ˆ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฉ”์„œ๋“œ์— @BindingAdapter ์ฃผ์„์„ ์ถ”๊ฐ€ํ•œ๋‹ค.

@BindingAdapter("app:timeAttrChanged") @JvmStatic fun setListeners(             view: MyView,             attrChange: InverseBindingListener     ) {         // Set a listener for click, focus, touch, etc. }

๋ฆฌ์Šค๋„ˆ์—๋Š” InverseBindingListener๊ฐ€ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํฌํ•จ๋˜๋ฉฐ InverseBindingListener๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ ์‹œ์Šคํ…œ์— ์†์„ฑ์ด ๋ณ€๊ฒฝ๋˜์—ˆ์Œ์„ ์•Œ๋ฆด ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด ์‹œ์Šคํ…œ์€ @InverseBindingAdapter ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ์„ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

6.2. Converters

View ๊ฐœ์ฒด์— ๊ฒฐํ•ฉ๋œ ๋ณ€์ˆ˜๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์ „์— ๋จผ์ € ํ˜•์‹ ์ง€์ •, ๋ณ€ํ™˜ ๋˜๋Š” ๋ณ€๊ฒฝ์„ ํ•ด์•ผ ํ•˜๋ฉด Converter ๊ฐœ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ๋‚ ์งœ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” EditText ๊ฐœ์ฒด๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

viewmodel.birthDate ์†์„ฑ์—๋Š” Long ์œ ํ˜•์˜ ๊ฐ’์ด ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ ๋ณ€ํ™˜๊ธฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ˜•์‹์„ ์ง€์ •ํ•ด์•ผ ํ•œ๋‹ค. ๋˜ํ•œ ์–‘๋ฐฉํ–ฅ ํ‘œํ˜„์‹์„ ์‚ฌ์šฉ ์ค‘์ด๋ฏ€๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ๋ฌธ์ž์—ด์„ ๋ฐฑ์—… ๋ฐ์ดํ„ฐ ์œ ํ˜•(์ด ์‚ฌ๋ก€์—์„œ๋Š” Long)์œผ๋กœ ๋‹ค์‹œ ๋ณ€ํ™˜ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์•Œ๋ ค์ฃผ๋Š” ์—ญ๋ณ€ํ™˜๊ธฐ๋„ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ์ด ํ”„๋กœ์„ธ์Šค๋Š” ๋ณ€ํ™˜๊ธฐ ์ค‘ ํ•˜๋‚˜์— @InverseMethod๋ฅผ ์ถ”๊ฐ€ํ•˜๊ณ  ์ด ์ฃผ์„์ด ์—ญ๋ณ€ํ™˜๊ธฐ๋ฅผ ์ฐธ์กฐํ•˜๋„๋ก ํ•จ์œผ๋กœ์จ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ๋‹ค. ์•„๋ž˜ ์ฝ”๋“œ ์Šค๋‹ˆํŽซ์€ ์ด ๊ตฌ์„ฑ์˜ ์˜ˆ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค.

object Converter {         @InverseMethod("stringToDate")         fun dateToString(             view: EditText, oldValue: Long,             value: Long         ): String {             // Converts long to String.         }

        fun stringToDate(             view: EditText, oldValue: String,             value: String         ): Long {             // Converts String to long.         } }

6.3. Infinite loops using two-way data binding

์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์‚ฌ์šฉํ•  ๋•Œ ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ์ฃผ์˜ํ•ด์•ผ ํ•œ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์†์„ฑ์„ ๋ณ€๊ฒฝํ•˜๋ฉด @InverseBindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๊ณ  ๊ฐ’์ด backing ์†์„ฑ์— ํ• ๋‹น๋˜๋ฉด ๊ฒฐ๊ณผ์ ์œผ๋กœ @BindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜์–ด @InverseBindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ์˜ ๋˜ ๋‹ค๋ฅธ ํ˜ธ์ถœ์ด ํŠธ๋ฆฌ๊ฑฐ ๋œ๋‹ค. ์ด๋Ÿฌํ•œ ์ด์œ ๋กœ @BindingAdapter๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ฃผ์„์ด ์ถ”๊ฐ€๋œ ๋ฉ”์„œ๋“œ์˜ ์ƒˆ ๊ฐ’๊ณผ ์ด์ „ ๊ฐ’์„ ๋น„๊ตํ•จ์œผ๋กœ์จ ๋ฐœ์ƒ ๊ฐ€๋Šฅํ•œ ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋Š๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.

6.4. Two-way attributes

์•„ํ•ด ํ‘œ์˜ ์†์„ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ ํ”Œ๋žซํผ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์–‘๋ฐฉํ–ฅ ๋ฐ์ดํ„ฐ ๊ฒฐํ•ฉ์„ ์ง€์›ํ•œ๋‹ค. ํ”Œ๋žซํผ์—์„œ ์ด ์ง€์›์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์— ๊ด€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ํ•ด๋‹นํ•˜๋Š” ๊ฒฐํ•ฉ ์–ด๋Œ‘ํ„ฐ์˜ ๊ตฌํ˜„์„ ์ฐธ์กฐ! ํด๋ž˜์Šค ์†์„ฑ ๊ฒฐํ•ฉ ์–ด๋Œ‘ํ„ฐ AdapterView android:selectedItemPosition android:selection AdapterViewBindingAdapter CalendarView android:date CalendarViewBindingAdapter CompoundButton android:checked CompoundButtonBindingAdapter DatePicker android:year android:month android:day DatePickerBindingAdapter NumberPicker android:value NumberPickerBindingAdapter RadioButton android:checkedButton RadioGroupBindingAdapter RatingBar android:rating RatingBarBindingAdapter SeekBar android:progress SeekBarBindingAdapter TabHost android:currentTab TabHostBindingAdapter TextView android:text TextViewBindingAdapter TimePicker android:hour android:minute TimePickerBindingAdapter

* ์ฐธ๊ณ :

https://medium.com/@PaperEd/android-how-to-databinding-169c78e7dc28 https://developer.android.com/topic/libraries/data-binding/expressions

โš ๏ธ **GitHub.com Fallback** โš ๏ธ