Data binding 변수의 특정 property가 변경될 경우 해당 변경을 반영하기 위해서는 변수를 새로 할당해 주어야 합니다. 만약 자주 변하는 property가 존재할 경우 계속 새로 data binding 변수를 할당하지 않고 해당 property를 observable하게 만들어 알아서 반영이 되도록 할 수 있습니다.
기본적으로 data binding library에서 제공되는 클래스들로 아래의 observable field 클래스들을 사용할 수 있습니다.
public class ObservableField<T> extends BaseObservableField implements Serializable {
static final long serialVersionUID = 1L;
private T mValue;
...
/**
* Set the stored value.
*
* @param value The new value
*/
public void set(T value) {
if (value != mValue) { // value changed
mValue = value;
notifyChange();
}
}
}
위의 클래스 중 generic한 ObservableField<T>의 동작 방식을 보면 set(value)로 값을 전달해주고 이전 값과 다르다면 내부에서 notifyChange()를 호출하여 새롭게 binding을 하는 것을 볼 수 있습니다.
Observable field의 값을 읽거나 새로운 값을 할당하기 위해서는 각각 set(value), get() method를 사용하거나 Kotlin extension을 이용하여 LiveData처럼 value property를 이용하여 사용할 수도 있습니다.
// kotlin extension property
var <T> ObservableField<T>.value
get() = this.get()
set(value) = this.set(value)
...
class MyClass(
val intData: ObservableField<Int>
)
...
myClass.intData.value = 10 // write new int
Log.d("TAG", myClass.intData.value.toString()) // read int
Observable은 단순 field뿐만이 아니라 collection의 List와 Map도 지원합니다. 동작방식은 위의 ObservableField<T>와 비슷하게 put(key, value), clear()등 내용에 변화가 생기면 내부에서 notify하여 새로 binding하게 됩니다.
ObservableMap은 key type이 reference type일 때 적합합니다.
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>
…
<!-- user.lastName is "Inc." -->
<TextView
android:text="@{user.lastName}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<!-- user.age is 17 -->
<TextView
android:text="@{String.valueOf(1 + (Integer)user.age)}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
ObservableList는 key type이 정수일 때 적합합니다.
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='@{user[Fields.LAST_NAME]}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:text='@{String.valueOf(1 + (Integer)user[Fields.AGE])}'
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
BaseObservable 클래스를 상속받아 사용자가 정의한 클래스의 property 값이 변경될 때 data binding이 자동으로 되도록 할 수 있습니다.
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)
}
}
생성된 ViewDataBinding 클래스에서 봤던 것 처럼 클래스가 BaseObservable을 상속받고, getter에 @Bindable annotation을 적용시키고, setter에서 값을 할당 후 notifyPropertyChanged를 이용하여 @Bindable annotation을 적용시킨 property가 변경된 것을 알려주어야 합니다.
DataBinding에 Observable뿐만 아니라 LiveData도 사용가능합니다. LiveData를 observe하기 위해 lifecycleOwner가 필요하므로 아래와 같이 지정해주어야 합니다.
class ViewModelActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
// Inflate view and obtain an instance of the binding class.
val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user)
// Specify the current activity as the lifecycle owner.
binding.lifecycleOwner = this
}
}
이렇게 lifecycleOwner를 지정해주면 ViewDataBinding.LiveDataListener에서 이전 owner를 제거하고 새로운 owner를 이용하여 observe하게 됩니다.
@Override
public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
LifecycleOwner previousOwner = getLifecycleOwner();
LifecycleOwner newOwner = lifecycleOwner;
LiveData<?> liveData = mListener.getTarget();
if (liveData != null) {
if (previousOwner != null) {
liveData.removeObserver(this);
}
if (newOwner != null) {
liveData.observe(newOwner, this);
}
}
if (newOwner != null) {
mLifecycleOwnerRef = new WeakReference<LifecycleOwner>(newOwner);
}
}
[1] "Work with observable data objects," Android Developers, last modified n.d., accessed May 25, 2022, https://developer.android.com/topic/libraries/data-binding/observability.
[2] "Bind layout views to Architecture Components," Android Developers, last modified n.d., accessed May 25, 2022, https://developer.android.com/topic/libraries/data-binding/architecture#livedata.