[Android] Data Binding - Part.2

Hwichan Ji·2021년 9월 6일
0

Android

목록 보기
7/7
post-thumbnail

📌 식별 가능한 데이터 객체와 Data Binding

Data Binding을 통해 객체, 필드 또는 컬렉션을 식별 가능하게 만들 수 있습니다.

식별 가능성
객체가 데이터 변경에 관해 다른 객체에 알릴 수 있는 기능

Data Binding을 사용하면 데이터 변경 시 리스너에 알리는 기능을 데이터 객체에 제공할 수 있는데, 이를 통해 UI를 자동으로 업데이트할 수 있습니다.

식별 가능한 필드

필드는 일반 Observable 클래스 및 Observable Primitive 클래스를 사용하여 식별 가능하게 만들 수 있습니다.

식별 가능한 필드는 단일 필드가 있는 독립적 객체입니다. Primitive 버전은 액세스 작업 중에 박싱 및 언박싱을 방지해야 하기 때문에 읽기 전용 속성으로 만들어야 합니다.

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

식별 가능한 컬렉션

식별 가능한 컬렉션은 키를 통해 접근할 수 있습니다.

키가 참조 유형일 때는 ObservableArrayMap 클래스가 유용합니다.

ObservableArrayMap<String, Any>().apply {
    put("firstName", "Google")
    put("lastName", "Inc.")
    put("age", 17)
}    
<data>
    <import type="android.databinding.ObservableMap" />
    <variable name="user" type="ObservableMap<String, Object>" />
</data>

<TextView
    android:text="@{user['lastName']}"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

키가 정수일 때는 ObsevableArrayList 클래스가 유용합니다.

ObservableArrayList<Any>().apply {
    add("Google")
    add("Inc.")
    add(17)
} 
<data>
    <import type="android.databinding.ObservableList"/>
    <import type="com.example.my.app.Fields"/>
    <variable name="user" type="ObservableList<Object>"/>
</data>

<TextView
    android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

식별 가능한 객체

Observable 인터페이스를 구현하면 식별 가능한 객체의 변경에 관한 알림을 받는 리스너를 등록할 수 있습니다.

Data Binding 라이브러리는 리스너 등록 메커니즘을 구현하는 BaseObservable 클래스를 제공합니다.

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)
        }
}

Data Binding은 데이터 바인딩에 사용된 리소스의 ID를 포함하는 모듈 패키지에 이름이 BR인 클래스를 생성합니다. Bindable 주석은 컴파일하는 동안 BR 클래스 파일에 항목을 생성합니다.

📌 바인딩 어댑터

바인딩 어댑터는 적절한 프레임워크를 호출하여 값을 설정하는 작업을 담당합니다.

속성 값 설정

결합된 값이 변경될 때마다 생성된 결합 클래스는 결합 표현식을 사용하여 속성 값을 설정해야 합니다.

자동 메서드 선택

라이브러리는 속성의 이름과 타입을 토대로 관련된 setter 메서드를 찾습니다. 예를 들어 android:text="@{user.name}" 표현식이 있는 경우 라이브러리는 user.getName()에서 반환한 타입을 허용하는 setText(arg) 메서드를 찾습니다.

맞춤 메서드 이름 지정

일부 속성에는 이름이 일치하지 않는 setter가 있습니다. 이러한 상황에서 속성은 BindingMethods 주석을 사용하여 setter와 연결될 수도 있습니다. 주석은 클래스와 함께 사용되며 이름이 바뀐 각 메서드에 하나씩 여러 BindingMethod 주석을 포함할 수 있습니다.

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

위 예에서 android:tint 속성은 setTint() 메서드가 아닌 setImageTintList(ColorStateList) 메서드와 연결됩니다.

맞춤 로직 제공

일부 속성에는 맞춤 결합 로직이 필요합니다. BindingAdapter 주석이 있는 정적 바인딩 어댑터 메서드를 사용하면 속성의 setter가 호출되는 방식을 맞춤설정할 수 있습니다.

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

바인딩 어댑터 메서드에서 첫 번째 매개변수는 속성과 연결된 뷰의 타입을 결정합니다. 두 번째 매개변수는 지정된 속성의 결합 표현식에서 허용되는 타입을 결정합니다.

개발자가 정의하는 바인딩 어댑터는 충돌이 발생하면 Android 프레임워크에서 제공하는 기본 어댑터보다 우선 적용됩니다.

여러 속성을 받는 어댑터도 있을 수 있습니다.

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

속성이 하나라도 설정될 때 어댑터가 호출되도록 하려면 다음 예에서와 같이 어댑터의 선택적 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("android:paddingLeft")
    fun setPaddingLeft(view: View, oldPadding: Int, newPadding: Int) {
        if (oldPadding != newPadding) {
            view.setPadding(padding,
                        view.getPaddingTop(),
                        view.getPaddingRight(),
                        view.getPaddingBottom())
        }
    }

이벤트 핸들러는 다음 예에서와 같이 하나의 추상 메서드가 있는 인터페이스 또는 추상 클래스에서만 사용할 수 있습니다.

@BindingAdapter("android:onLayoutChange")
fun setOnLayoutChangeListener(
        view: View,
        oldValue: View.OnLayoutChangeListener?,
        newValue: View.OnLayoutChangeListener?
) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        if (oldValue != null) {
                view.removeOnLayoutChangeListener(oldValue)
        }
        if (newValue != null) {
            view.addOnLayoutChangeListener(newValue)
        }
    }
}

Reference

profile
안드로이드 개발자를 꿈꾸는 사람

0개의 댓글