양방향 데이터 결합

다음과 같이 단방향 데이터 결합을 사용하면 속성에 값을 설정하고 이 속성의 변경에 반응하는 리스너를 설정할 수 있습니다.

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

다음과 같이 양방향 데이터 결합은 이 프로세스의 바로가기를 제공합니다.

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

'=' 기호가 포함된 @={} 표기법은 속성과 관련된 데이터 변경사항을 받는 동시에 사용자 업데이트를 수신합니다.

백업 데이터의 변경에 대응하기 위해 다음 코드 스니펫에서와 같이 레이아웃 변수를 Observable 일반적으로 BaseObservable의 구현으로 만들고 @Bindable 주석을 사용할 수 있습니다.

Kotlin

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

자바

    public class LoginViewModel extends BaseObservable {
        // private Model data = ...

        @Bindable
        public Boolean getRememberMe() {
            return data.rememberMe;
        }

        public void setRememberMe(Boolean value) {
            // 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()라는 이름을 사용합니다.

BaseObservable@Bindable 사용에 관한 자세한 내용은 식별 가능한 데이터 개체 작업을 참조하세요.

맞춤 속성을 사용한 양방향 데이터 결합

플랫폼은 앱의 일부로 사용할 수 있는 가장 일반적인 양방향 속성 및 변경 리스너의 양방향 데이터 결합 구현을 제공합니다. 맞춤 속성으로 양방향 데이터 결합을 사용하려면 @InverseBindingAdapter@InverseBindingMethod 주석을 활용해야 합니다.

예를 들어 MyView라는 맞춤 뷰에서 "time" 속성에 양방향 데이터 결합을 사용하려면 다음 단계를 완료해야 합니다.

  1. 초기 값을 설정하고 값이 변경될 때 업데이트하는 메서드에 @BindingAdapter를 사용하여 주석을 추가합니다.

    Kotlin

        @BindingAdapter("time")
        @JvmStatic fun setTime(view: MyView, newValue: Time) {
            // Important to break potential infinite loops.
            if (view.time != newValue) {
                view.time = newValue
            }
        }

    자바

        @BindingAdapter("time")
        public static void setTime(MyView view, Time newValue) {
            // Important to break potential infinite loops.
            if (view.time != newValue) {
                view.time = newValue;
            }
        }
  2. 뷰에서 값을 읽는 메서드에 @InverseBindingAdapter를 사용하여 주석을 추가합니다.

    Kotlin

        @InverseBindingAdapter("time")
        @JvmStatic fun getTime(view: MyView) : Time {
            return view.getTime()
        }

    자바

        @InverseBindingAdapter("time")
        public static Time getTime(MyView view) {
            return view.getTime();
        }

이 시점에서 데이터 결합은 데이터가 변경될 때 해야 할 작업(@BindingAdapter를 사용하여 주석을 추가한 메서드 호출)과 뷰 속성이 변경될 때 호출할 대상(InverseBindingListener 호출)을 알고 있습니다. 하지만 속성이 언제 어떻게 변경되는지는 알 수 없습니다.

속성의 변경 시기 또는 방식을 알기 위해서는 뷰에 리스너를 설정해야 합니다. 리스너는 맞춤 뷰와 연결된 맞춤 리스너이거나 포커스 상실 또는 텍스트 변경과 같은 제네릭 이벤트일 수 있습니다. 다음과 같이 속성 변경의 리스너를 설정하는 메서드에 @BindingAdapter 주석을 추가합니다.

Kotlin

    @BindingAdapter("app:timeAttrChanged")
    @JvmStatic fun setListeners(
            view: MyView,
            attrChange: InverseBindingListener
    ) {
        // Set a listener for click, focus, touch, etc.
    }

자바

    @BindingAdapter("app:timeAttrChanged")
    public static void setListeners(
            MyView view, final InverseBindingListener attrChange) {
        // Set a listener for click, focus, touch, etc.
    }
    

리스너에는 InverseBindingListener가 매개변수로 포함됩니다. InverseBindingListener를 사용하면 데이터 결합 시스템에 속성이 변경되었음을 알릴 수 있습니다. 그러면 시스템은 @InverseBindingAdapter 등을 사용하여 주석이 추가된 메서드 호출을 시작할 수 있습니다.

실제로 이 리스너에는 단방향 데이터 결합을 위한 리스너를 비롯하여 중요한 로직이 포함되어 있습니다. 예를 보려면 텍스트 속성 변경을 위한 어댑터 TextViewBindingAdapter를 참조하세요.

변환기

View 개체에 결합된 변수를 표시하기 전에 먼저 형식 지정, 변환 또는 변경을 해야 하면 Converter 개체를 사용하면 됩니다.

예를 들어 날짜를 보여주는 EditText 개체를 사용합니다.

<EditText
        android:id="@+id/birth_date"
        android:text="@={Converter.dateToString(viewmodel.birthDate)}"
    />
    

viewmodel.birthDate 속성에는 Long 유형의 값이 포함되어 있으므로 변환기를 사용하여 형식을 지정해야 합니다.

또한 양방향 표현식을 사용 중이므로 사용자가 제공한 문자열을 백업 데이터 유형(이 사례에서는 Long)으로 다시 변환하는 방법을 라이브러리에 알려주는 역변환기도 있어야 합니다. 이 프로세스는 변환기 중 하나에 @InverseMethod 주석을 추가하고 이 주석이 역변환기를 참조하도록 함으로써 완료할 수 있습니다. 다음 코드 스니펫은 이 구성의 예를 보여줍니다.

Kotlin

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

자바

    public class Converter {
        @InverseMethod("stringToDate")
        public static String dateToString(EditText view, long oldValue,
                long value) {
            // Converts long to String.
        }

        public static long stringToDate(EditText view, String oldValue,
                String value) {
            // Converts String to long.
        }
    }

양방향 데이터 결합을 사용하는 무한 루프

양방향 데이터 결합을 사용할 때 무한 루프가 발생하지 않도록 주의해야 합니다. 사용자가 속성을 변경하면 @InverseBindingAdapter를 사용하여 주석이 추가된 메서드가 호출되고 값이 backing 속성에 할당됩니다. 그러면 결과적으로 @BindingAdapter를 사용하여 주석이 추가된 메서드가 호출되어 @InverseBindingAdapter를 사용하여 주석이 추가된 메서드의 또 다른 호출이 트리거됩니다.

이러한 이유로 @BindingAdapter를 사용하여 주석이 추가된 메서드의 새 값과 이전 값을 비교함으로써 발생 가능한 무한 루프를 끊는 것이 중요합니다.

양방향 속성

다음 표의 속성을 사용할 때 플랫폼은 기본적으로 양방향 데이터 결합을 지원합니다. 플랫폼에서 이 지원을 제공하는 방식에 관한 자세한 내용은 해당하는 결합 어댑터의 구현을 참조하세요.

클래스 속성 결합 어댑터
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

참고 자료

데이터 결합에 관해 자세히 알아보려면 다음 참고 자료를 참조하세요.

샘플

Codelab

블로그 게시물