[개념] 데이터 바인딩(data Binding) (4)

쓰리원·2022년 5월 10일
0

binding 정리

목록 보기
5/6
post-thumbnail

binding 글 링크

개념-데이터-바인딩data-Binding (1)
개념-데이터-바인딩data-Binding (2)
개념-데이터-바인딩data-Binding (3)
개념-데이터-바인딩data-Binding (4)
개념-데이터-바인딩data-Binding (5)

1. data binding 라이브러리 사용

1. Work with observable data objects

databinding 에서 임의의 데이터 객체를 사용하여 레이아웃과 바인딩을 구현 할 수 있지만 바인딩 하고 있는 객체의 값이 변경 되어도 UI는 업데이트 가 안됩니다. 데이터가 변경 되었을 때 이를 알려주는 기능을 데이터 객체에 부여하면 databinding 의 장점을 극대화 시킬 수 있습니다. databinding 은 데이터 변경에 대응하기 위한 세 가지 유형 Observable objects, Observable fields, Observable collections이 있습니다.

2. Observable Objects

바인딩 하려는 객체에 androidx.databinding 패키지의 Observable 인터페이스를 구현하면 해당 객체에 단일 리스너를 연결하여 그 객체에 모든 속성의 변경사항을 수신 할 수 있게 됩니다. 편의를 위해 BaseObservable 클래스를 제공하고 있으며, 원하는 객체에 BaseObservable 클래스를 확장하여 사용 할 수 있습니다.

내부적으로 리스너를 추가/해제 하는 메커니즘을 갖고 있지만 최종적으로 데이터 변경에 대해 처리를 하기 위해서는 해당 필드의 접근자 메소드에 androidx.databinding 패키지의 Bindable annotation 을 추가하고 설정자 메서드에서 이를 알림으로써 구현 할 수 있습니다. 예제코드를 살펴보겠습니다.

data class User(private var firstName: String,
                private var lastName: String) : BaseObservable() {

    //데이터 변경 시 알림을 수신 하고자 하는 필드에 Bindable annotation 추가
    @Bindable
    fun getFirstName() = firstName

    @Bindable
    fun getLastName() = lastName

    fun setFirstName(firstName: String) {
        this.firstName = firstName

        //데이터 변경을 알리기 위해 notifyPropertyChanged() 호출
        notifyPropertyChanged(BR.firstName)
    }

    fun setLastName(lastName: String) {
        this.lastName = lastName
        notifyPropertyChanged(BR.lastName)
    }
}

getFirstName() , getLastName() 에 Bindable annotation 을 설정 하였습니다. Bindable annotation 을 설정한 필드는 컴파일 시 BR 이라는 클래스에 filedId 를 자동 생성하게 되는데, 이후 데이터 변경 알림을 위해 notifyPropertyChanged(filedId : Int) 를 호출 할 때 파라미터로 사용 됩니다. (BR 클래스 파일은 모듈 패키지 내에 생성 됩니다) 그리고 setFirstName() , setLastName() 내에서 notifyPropertyChanged() 를 호출하여 값이 변경 되었음을 알리게 됩니다.

위 예제코드에서는 data class 로 작성하였는데 만약 일반 class 로 작성한다면 아래와 같은 방법으로도 사용 할 수 있습니다. 참고하시기 바랍니다.

    class User : BaseObservable() {

        @get:Bindable
        var firstName: String = ""
            set(value) {
                field = value
                notifyPropertyChanged(BR.firstName)
            }

        @get:Bindable
        var lastName: String = ""
            set(value) {
                field = value
                notifyPropertyChanged(BR.lastName)
            }
    }

3. Observable Objects 구현 예제 코드

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="activity"
            type="com.project.databindingobservable.MainActivity" />

        <variable
            name="user"
            type="com.project.databindingobservable.User" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:textSize="50sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}"
            tools:text="firstName"/>

        <TextView
            android:textSize="50sp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}"
            tools:text="lastName"/>

    </LinearLayout>
</layout>
class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this,R.layout.activity_main)
        binding.activity = this@MainActivity
        setObservable()
    }

    fun setObservable() {
        var user : User = User()
        binding.user = user
        user.firstName = "Test"
        user.lastName = "User"

        Handler().postDelayed(Runnable {
            run {
                user.firstName = "Test modify"
                user.lastName = "User modify"
            }
        },5000)
    }
}

class User : BaseObservable() {
    @get:Bindable
    var firstName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }
    @get:Bindable
    var lastName: String = ""
        set(value) {
            field = value
            notifyPropertyChanged(BR.lastName)
        }
}

코드 링크 : https://github.com/ilil1/dataBindingObservable

4. Observable Fields

위에서 데이터 객체에 Observable interface 를 구현하여 사용하는 방식을 알아보았습니다. 다음으로 Databinding library 에서는 각 필드단위로 Observable를 구현 할 수 있는 Observable Fileds를 제공 합니다. 제공하는 Observable Fileds 목록은 아래와 같습니다.

ObservableField
ObservableBoolean
ObservableByte
ObservableChar
ObservableShort
ObservableInt
ObservableLong
ObservableFloat
ObservableDouble
ObservableParcelable

예제 코드를 보겠습니다.

class User {
    val firstName: ObservableField<String> = ObservableField()
    val lastName: ObservableField<String> = ObservableField()
    val age: ObservableInt = ObservableInt()
}

이렇게 Observable 을 구현하려는 필드에 위와 같이 선언 해서 사용 할 수 있습니다. ObservableField는 박싱/언박싱 방지를 위해 final(kotlin은 val)로 선언해야 합니다.

ObservableField 속성 Access 방법으로 (기존 setter/getter가 아닌 set() / get()접근자 메서드 사용) : 기존 Setter/Getter 메서드가 아닌 set() / get() 접근자 메서드를 사용합니다

ObservableField 타입의 속성이여서 값 변경시 자동으로 View에 알림(notiy)를 줘서 UI업데이트가 가능합니다. Observable 인터페이스를 구현한 Observable Object와 달리 사용자가 notify 메서드 호출 할 필요가 없습니다.

코틀린

user.firstName = "Google"
val age = user.age

자바

user.firstName.set("Google");
int age = user.age.get();

5. Observable Collection

마지막으로 databinding library 는 Observable Collection 을 제공 합니다. 제공하는 Collection 은 아래와 같습니다.

ObservableArrayList
ObservableArrayMap<K,V>
ObservableMap<K,V>

ObservableArrayMap 을 사용해보겠습니다. 키가 String 과 같은 참조 형식 일때 사용하기 적절합니다

ObservableArrayMap<String, Object> user = new ObservableArrayMap<>();
user.put("firstName", "Google");
user.put("lastName", "Inc.");
user.put("age", 17);

이제 레이아웃을 아래와 같이 수정하여 바인딩 할 수 있습니다.

<data>
    <import type="android.databinding.ObservableMap"/>
    <variable name="user" type="ObservableMap&lt;String, Object&gt;"/>
</data><TextView
   android:text='@{user["lastName"]}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>
<TextView
   android:text='@{String.valueOf(1 + (Integer)user["age"])}'
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"/>

ObservableList 의 예제도 한번 살펴 보겠습니다. List 에 값을 저장하고 바인딩 식에서 index 를 통해 바인딩이 가능합니다.

<data>
    <import type="android.databinding.ObservableList"/>
    <variable name="user" type="ObservableList&lt;Object&gt;"/>
</data>
....
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user[0]}" />

<TextView
    android:id="@+id/test1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{user[1]}" />
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.user = ObservableArrayList<Any>().apply{
    add("Google")
    add("Inc.")
    add(17)
}

kotlin 에서는 Object 대신 Any 라는 이름을 사용하지만 레이아웃에 variable 을 생성할때는 Object 로 선언해야 하는 점을 주의 하시기 바랍니다.

2. reference

https://developer.android.com/topic/libraries/data-binding/start
https://developer.android.com/topic/libraries/data-binding
https://developer.android.com/topic/libraries/data-binding/expressions
https://developer.android.com/topic/libraries/data-binding/observability

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글