Skip to content

Data Binding

leekiljae2019 edited this page Jan 13, 2020 · 3 revisions

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

Clone this wiki locally