Binding layout views to schema components
The AndroidX library contains architecture components, which can be used to design reliable, testable and maintainable applications. The data binding library can work seamlessly with architecture components to further simplify the development of the interface. The layout in the application can be bound to the data in the architecture components, which can help you manage the life cycle of the interface controller and notify the data changes.
This page describes how to integrate architecture components into your application to further highlight the advantages of using data binding libraries.
Use LiveData to notify the interface of data changes
You can use the LiveData object as the data binding source to automatically notify the interface of data changes. For a detailed understanding of this architecture component, see LiveData overview.
Unlike objects that implement Observable, such as Observable fields, LiveData objects understand the lifecycle of observers that subscribe to data changes. There are many benefits to understanding this. For details, please refer to the advantages of using LiveData. In Android Studio version 3.1 and later, you can replace Observable fields with LiveData objects in data binding code.
To use a LiveData object with a binding class, you need to specify the lifecycle owner to define the scope of the LiveData object. The following example specifies the Activity as the lifecycle owner after the binding class is instantiated:
Kotlin
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.setLifecycleOwner(this) } }
Java
class ViewModelActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Inflate view and obtain an instance of the binding class. UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user); // Specify the current activity as the lifecycle owner. binding.setLifecycleOwner(this); } }
You can use the ViewModel component to bind data to a layout as described in using the ViewModel to manage data in the interface. In the ViewModel component, you can use LiveData objects to transform data or merge multiple data sources. The following example shows how to convert data in ViewModel:
Kotlin
class ScheduleViewModel : ViewModel() { val userName: LiveData init { val result = Repository.userName userName = Transformations.map(result) { result -> result.value } } }
Java
class ScheduleViewModel extends ViewModel { LiveData username; public ScheduleViewModel() { String result = Repository.userName; userName = Transformations.map(result, result -> result.value); } }
Use ViewModel to manage interface related data
The data binding library can work seamlessly with ViewModel components, which expose the data observed by the layout and respond to its changes. By combining ViewModel components with data binding libraries, you can move interface logic out of the layout and into these components for testing. The data binding library ensures that views are bound or unbound from data sources when needed. Most of the rest of the work is to ensure that you disclose the correct data. For more information about this schema component, see ViewModel overview.
To use the ViewModel component with the data binding library, you must instantiate the component inherited from the ViewModel class, obtain the instance of the binding class, and assign your ViewModel component to the properties in the binding class. The following example shows how to use components with libraries:
Kotlin
class ViewModelActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Obtain the ViewModel component. val userModel: UserModel by viewModels() // Inflate view and obtain an instance of the binding class. val binding: UserBinding = DataBindingUtil.setContentView(this, R.layout.user) // Assign the component to a property in the binding class. binding.viewmodel = userModel } }
Java
class ViewModelActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { // Obtain the ViewModel component. UserModel userModel = new ViewModelProvider(this).get(UserModel.class); // Inflate view and obtain an instance of the binding class. UserBinding binding = DataBindingUtil.setContentView(this, R.layout.user); // Assign the component to a property in the binding class. binding.viewmodel = userModel; } }
In your layout, use the binding expression to assign the properties and methods of the ViewModel component to the corresponding view, as shown in the following example:
<CheckBox android:id="@+id/rememberMeCheckBox" android:checked="@{viewmodel.rememberMe}" android:onCheckedChanged="@{() -> viewmodel.rememberMeChanged()}" />
Use Observable ViewModel to better control the binding adapter
You can use the ViewModel component that implements Observable to notify other application components of data changes, which is similar to using LiveData objects.
In some cases, you may prefer to use the ViewModel component that implements the Observable interface rather than the LiveData object, even if it will lose the life cycle management function of LiveData. Using the ViewModel component that implements Observable allows you to better control the binding adapter in your application. For example, this mode allows you to better control the notification when data changes. You can also specify a custom method to set property values in two-way data binding.
To implement an Observable ViewModel component, you must create a class that inherits from the ViewModel class and implements the Observable interface. You can use the addOnPropertyChangedCallback() and removeOnPropertyChangedCallback() methods to provide custom logic for observers to subscribe to or unsubscribe from notifications. You can also provide custom logic that runs when a property changes in the notifyPropertyChanged() method. The following code example shows how to implement an Observable ViewModel:
Kotlin
/** * A ViewModel that is also an Observable, * to be used with the Data Binding Library. */ open class ObservableViewModel : ViewModel(), Observable { private val callbacks: PropertyChangeRegistry = PropertyChangeRegistry() override fun addOnPropertyChangedCallback( callback: Observable.OnPropertyChangedCallback) { callbacks.add(callback) } override fun removeOnPropertyChangedCallback( callback: Observable.OnPropertyChangedCallback) { callbacks.remove(callback) } /** * Notifies observers that all properties of this instance have changed. */ fun notifyChange() { callbacks.notifyCallbacks(this, 0, null) } /** * Notifies observers that a specific property has changed. The getter for the * property that changes should be marked with the @Bindable annotation to * generate a field in the BR class to be used as the fieldId parameter. * * @param fieldId The generated BR id for the Bindable field. */ fun notifyPropertyChanged(fieldId: Int) { callbacks.notifyCallbacks(this, fieldId, null) } }
Java
/** * A ViewModel that is also an Observable, * to be used with the Data Binding Library. */ class ObservableViewModel extends ViewModel implements Observable { private PropertyChangeRegistry callbacks = new PropertyChangeRegistry(); @Override protected void addOnPropertyChangedCallback( Observable.OnPropertyChangedCallback callback) { callbacks.add(callback); } @Override protected void removeOnPropertyChangedCallback( Observable.OnPropertyChangedCallback callback) { callbacks.remove(callback); } /** * Notifies observers that all properties of this instance have changed. */ void notifyChange() { callbacks.notifyCallbacks(this, 0, null); } /** * Notifies observers that a specific property has changed. The getter for the * property that changes should be marked with the @Bindable annotation to * generate a field in the BR class to be used as the fieldId parameter. * * @param fieldId The generated BR id for the Bindable field. */ void notifyPropertyChanged(int fieldId) { callbacks.notifyCallbacks(this, fieldId, null); } }