데이터 바인딩 라이브러리는 프로그래매틱 방식이 아니라 선언적 형식으로 레이아웃의 UI 구성요소를 앱의 데이터 소스와 결합할 수 있는 지원 라이브러리입니다.기존의 findViewById를 사용할 필요가 없습니다. LiveData를 액티비티에서 따로 observe하는 코드를 작성할 필요가 없습니다.
build.gralde(:app)에 dataBinding을 선언해 줍니다.
android {
...
dataBinding {
enabled = true
}
}
기존 레이아웃을 layout 으로 감싸줍니다. 그리고 내가 사용할 뷰모델을 data 안의 variable에 등록해줍니다.
그리고 뷰모델 안의 변수를 textView에 표시하고 싶을 땐 "@{}" 형식으로 사용해줍니다.
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<variable
name="vm"
type="com.example.counterapp.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.liveCounter.toString()}"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btn_increase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="30dp"
android:text="increase"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
메인 액티비티에서는 기존의 setContentView 대신 아래와 같이 DataBindingUtil.setContentView()를 사용해줍니다.
그리고 lifecyclerOwner를 현재 액티비티에 맞춰주고 레이아웃에 data의 variable의 vm에 어떤 뷰모델을 설정할지 작성합니다.
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val factory = MainViewModelFactory(100, this)
val mainViewModel by viewModels<MainViewModel> { factory }
binding.lifecycleOwner = this
binding.vm = mainViewModel
binding.btnIncrease.setOnClickListener {
mainViewModel.increase()
}
}
}
데이터 바인딩 라이브러리를 사용하면 BindingAdpater를 사용할 수 있습니다. 바인딩 어댑터는 적절한 프레임워크를 호출하여 값을 설정하는 작업을 담당합니다.
build.gradle(:app)파일에 'kotlin-kapt' 플러그인을 추가해줍니다.
plugins {
...
id 'kotlin-kapt'
}
원하는 메서드 이름을 지정해주고 View의 확장함수에 함수를 지정합니다. 함수 안에 원하는 로직을 작성합니다.
확장함수를 쓰지않고 매개변수로 View를 전달받아 사용해도 됩니다.
@BindingAdapter("setProgressBar")
fun ProgressBar.setProgress(counter: Int) {
this.progress = counter
}
두 개 이상의 메서드를 같이 사용하고 싶다면 아래와 같이 사용합니다.
@BindingAdapter(value = ["setProgressBar", "android:max"], requireAll = true)
fun ProgressBar.setProgress(counter: Int, max: Int) {
this.progress = (counter * 2).coerceAtMost(max)
}
해당 뷰에서 bindingAdpater에서 설정한 메서드 이름으로 사용하고 값을 전달합니다.
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
setProgressBar="@{vm.liveCounter}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:max="@{100}"
app:layout_constraintBottom_toTopOf="@id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
뷰에서도 뷰모델의 값을 변경할 필요가 있을 때 양방향 데이터 바인딩을 사용합니다.
뷰모델에 값을 하나 선언해줍니다
val hasChecked : MutableLiveData<Boolean> = MutableLiveData(false)
xml 파일에서 android:checked부분에서 "@={}" 형태로 사용해주면 양방향 데이터 바인딩이 된다. 이제 뷰에서 checkBox를 클릭하면 뷰모델의 hasChecked도 값이 바뀐다.
<CheckBox
android:id="@+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="@={vm.hasChecked}"
android:text="@{vm.hasChecked.toString()}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_increase" />