안드로이드 DataBinding (데이터 바인딩)

|

데이터 바인딩이란?

Part of Android Jetpack
https://developer.android.com/topic/libraries/data-binding 를 참고하여 작성하였습니다.

아래의 코드는 안드로이드 UI 프로그래밍시 많이 보는 코드이다.

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

데이터 바인딩을 사용하면 위의 자바 코드를 부르는것을 생략 가능하다.

<TextView
    android:text="@{viewmodel.userName}" />

데이터 바인딩 사용하기

환경설정

build.gradle에 dataBinding enabled로 설정하기

android {
    ...
    dataBinding {
        enabled = true
    }
}

레이아웃 파일

데이터 바인딩 레이아웃 파일은 일반 레이아웃 파일과 약간 다르다. root 태그를 layout으로 감싸고 그 다음 data와 view 엘리먼트가 따른다.

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
   <data>
       <variable name="user" type="com.example.User"/>
   </data>
   <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}"/>
       <TextView android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:text="@{user.lastName}"/>
   </LinearLayout>
</layout>

데이터 바인딩하기

각각의 레이아웃 파일마다 바인딩 클래스가 만들어진다. 기본적으로 클래스의 이름은 레이아웃 파일의 이름에 따른다. 만약 activity_main.xml 이었다면 ActivityMainBinding 이라는 이름으로 생기게 된다.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding: ActivityMainBinding = DataBindingUtil.setContentView(
            this, R.layout.activity_main)

    binding.user = User("Test", "User")
}

Collection Data 바인딩하기

<data>
    <import type="android.util.SparseArray"/>
    <import type="java.util.Map"/>
    <import type="java.util.List"/>
    <variable name="list" type="List&lt;String>"/>
    <variable name="sparse" type="SparseArray&lt;String>"/>
    <variable name="map" type="Map&lt;String, String>"/>
    <variable name="index" type="int"/>
    <variable name="key" type="String"/>
</data>
…
android:text="@{list[index]}"
…
android:text="@{sparse[index]}"
…
android:text="@{map[key]}"

바인딩 어댑터

바인딩시 몇 속성들은 setter를 가지고 있지 않을 수 있다. 이런 상황에는 BindingMethods 어노테이션을 사용한다. 이 어노테이션은 클래스에 쓰인다.
아래 예시에서는 android:tint 라는 attribute가 setImageTintList라는 메소드와 연관되어 있다. 대부분의 경우에는 안드로이드 프레임워크 클래스들의 세터를 다시 세팅할 필요는 없다.

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

양방향 데이터 바인딩

단방향 데이터 바인딩을 사용하면, 아래와 같이 변경에 반응하는 리스너와 값을 세팅할 수 있다.

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@{viewmodel.rememberMe}"
    android:onCheckedChanged="@{viewmodel.rememberMeChanged}"
/>

양방향 바인딩은 위의 코드를 아래처럼 줄여 준다. @={} 을 사용하여 데이터 변경과 프로퍼티의 변경을 받고 유저의 업데이트를 동시에 받을 수 있다.

<CheckBox
    android:id="@+id/rememberMeCheckBox"
    android:checked="@={viewmodel.rememberMe}"
/>

Observable

데이터의 수정이 일어났을 때 그것을 감지할수 있게 하는 것이다.
observable 클래스에는 세가지 타입이 있는데, object, fields, collectios이다.
이중 하나의 observable 데이터에 변화가 생기면 UI가 자동으로 업데이트된다.

뒤에서 변하는 변화를 감지하기 위해서 레이아웃 변수에 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)
        }
    }
}