Jetpack AAC Series:
(1) Lifecycle will be fully mastered!
“Finally get it” series: Jetpack AAC complete analysis (2) Fully master LiveData!
“Finally get it” series: Jetpack AAC complete analysis (3) fully master the ViewModel!
“Finally Get it” series: Jetpack AAC Complete Analysis (iv) MVVM Architecture Exploration!
Jetpack AAC DataBinding
Welcome to follow my public account, wechat search Hu Feiyang, article updates can be the first time to receive.
The first four articles introduced the underlying components of the Jetpack architectural components and their combined application: the Jetpack MVVM architectural pattern, which has been largely standardized for development. But in addition to Lifecycle, LivaData, ViewModel, the Jetpack architecture components are:
- WorkManager, which manages tasks for background work, even when the application exits or restarts.
- Paging, Paging library, load part of the data as needed.
- Startup, for App Startup speed optimization, but only for library developers, guo Lin has more details.
- DataStore, which replaces SharedPreferences, is currently in the Alpha phase.
- DataBinding, which binds the interface components in the layout directly to the data source, provides bidirectional binding, and advanced binding adaptation.
- ViewBinding, in place of findViewById, and DataBinding also contains ViewBinding capabilities.
- Room, to achieve local storage database management, support LiveData.
Currently, WorkManager, Paging, and Startup are not required in terms of the need for learning and functionality of the library, DataStore has not been officially released, and ViewBinding capability is included in DataBinding. Room, which is similar in functionality and performance to GreenDAO, has the advantage of supporting LivaData, but projects that already use GreenDAO do not need to switch to Room.
DataBinding is a controversial library, which is the focus of this article and will bring you a new understanding of the misunderstood DataBinding.
First, re-recognize DataBinding
DataBinding use method, refer to the official documents can be introduced in detail, here is no longer moving. (It’s also good to find a MOOCs video: Introductory, Advanced)
1.1 The nature of DataBinding
The idea of DataBinding, like I used to, is to write logic in XML:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='@ {! isFemale? User.name + ": male ": user.name + ": female "}'/>
Copy the code
You see the XML using a teradata expression to evaluate the value required by the view and think, “DataBinding doesn’t work! If you write expression logic in XML, you can’t debug it. If you write the logic in XML, XML takes over the role of Presenter/ViewModel and becomes confusing.”
If you write your logic in XML, it’s true: XML is undebuggable, and responsibilities are really cluttered.
But is that what DataBinding is all about?
1.1.1 DataBinding before
Before DataBinding existed, you would reference a view to change it:
TextView textView = findViewById(R.id.sample_text);
if(textView ! =null&& viewModel ! =null) {
textView.setText(viewModel.getUserName());
}
Copy the code
To reference the view, you have to nullify it. Neither textView nor viewModel can be null. Why should textView be empty? In one case, R.I.D.sample_text is defined in another page; One case is that there are two different horizontal and vertical layouts of controls, such as the landscape screen exists this textView control, and portrait screen does not, then it needs to do short processing.
There are many pages and controls in the App, and a control may be called from many places, which will have the possibility of a null pointer, so how to completely avoid it?
1.1.2 Data Binding
DataBinding, meaning DataBinding, is the binding of controls in a layout to observable data.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
Copy the code
The TextView is actually there in the layout, so you don’t have to empty it. DataBinding also automatically handles whether user is empty: in the expression @{user.name}, if user is Null, the default value of Null is assigned to user.name.
And when the user.name is set to a new value, the control bound to that data is notified and refreshed. In other words, the only change after using DataBinding is that you don’t have to manually call the view to set the new state; you just set the data itself.
So DataBinding is not about moving UI logic into XML and making it hard to debug. It’s just about binding the data, the UI control to the end-state data it needs. Terminal data refers to data directly required by the UI control (UI data), string values, int values, etc., rather than a piece of logic (otherwise it would be called a LogicBinding, although DataBinding supports logical expressions).
Now that you have defined the boundaries of DataBinding’s responsibilities, you should know what to do with the original logic code, but instead of textView.settext (user.name), you need to go straight to user.setname ().
So DataBinding is essentially a binding of end-state data to UI controls, with the following advantages:
- Instead of calling the control multiple times, you just need to set the data where you originally called it
- 1 extension, without manual empty
- 1 extension, without writing the template code findViewById at all
- Also, when DataBinding is introduced, the original UI logic doesn’t need to be changed, just set the end state data
As mentioned in the previous article, the Jetpack MVVM architecture is data-driven in nature, which means that control state and data are managed separately in the ViewModel, and the ViewModel layer is only responsible for changes in the state data itself. The ViewModel doesn’t care which views the data is bound to in the layout, whether or not it is bound to, and how.
How does a control get notified and update its status?
DataBinding manages control refresh status through observer mode. When the status data changes, you simply complete the setValue manually, which informs the DataBinding to refresh the data-bound control.
However, putting logic in XML, as mentioned at the beginning of this article, is not recommended. Data values should directly reflect the desired results of UI controls, rather than being placed in XML as logical conditions. So DataBinding is not responsible for the UI logic, which is still written where it was written, except that the way the control instance was called to refresh the state is now data-driven. This is the core goal of DataBinding.
1.2 Example – Binding list data
For example, the page displays a list of users, with two buttons for adding and removing users:
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="clickPresenter"
type="com.hfy.demo01.module.jetpack.databinding.ListActivity.ClickPresenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".module.jetpack.databinding.ListActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Add user"
android:onClick="@{clickPresenter::addUser}"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Remove the user"
android:onClick="@{clickPresenter::removeUser}"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_user_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
Copy the code
We know that the RecyclerView displays the list data, is through the Adapter for each data is set separately, that is, the User is bound to the Item XML:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.hfy.demo01.module.jetpack.databinding.bean.User" />
</data>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="50dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.level}"/>
</LinearLayout>
</layout>
Copy the code
Let’s see how this is handled in the Activity:
public class ListActivity extends AppCompatActivity {
private ActivityListBinding mViewDataBinding;
private static UserListAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_list);
mViewDataBinding.rvUserList.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
mAdapter = new UserListAdapter();
mAdapter.setNewInstance(getUserList());
mViewDataBinding.rvUserList.setAdapter(mAdapter);
mViewDataBinding.setClickPresenter(new ClickPresenter());
}
// Pretend to call the ViewModel capability to get user data
private List<User> getUserList(a) {
List<User> list = new ArrayList<>();
list.add(new User("Xiao Ming"."Lv1"));
list.add(new User("Little red"."Lv2"));
list.add(new User("Little q"."Lv3"));
list.add(new User("A"."Lv4"));
return list;
}
// Click listen to handle
public class ClickPresenter {
public void addUser(View view) {
Toast.makeText(ListActivity.this."addUser", Toast.LENGTH_SHORT).show();
mAdapter.addData(new User("Z"."Lv5"));
}
public void removeUser(View view) {
Toast.makeText(ListActivity.this."removeUser", Toast.LENGTH_SHORT).show();
mAdapter.remove(0); }}private static class UserListAdapter extends BaseQuickAdapter<User.UserItemViewHolder> {
public UserListAdapter(a) {
super(R.layout.item_user);
}
@Override
protected void convert(@NonNull UserItemViewHolder holder, User user) {
// The essence is 1, you don't need to do a setText, etc
holder.getItemUserBinding().setUser(user);
holder.getItemUserBinding().executePendingBindings();
// When the DataBinding is not a concrete class, it is just a ViewDataBinding, then we use setVariable
// holder.getViewDataBinding().setVariable(BR.user, user);
// holder.getViewDataBinding().executePendingBindings();}}private static class UserItemViewHolder extends BaseViewHolder {
// The essence of the 2, only need to hold a binding, do not findViewById
private final ItemUserBinding binding;
// private final ViewDataBinding binding2;
public UserItemViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
// binding2 = DataBindingUtil.bind(view);
}
public ItemUserBinding getItemUserBinding(a) {
return binding;
}
// public ViewDataBinding getViewDataBinding() {
// return binding2;
/ /}}}Copy the code
RecyclerView initialization, call ViewModel to obtain data, these processing and logic is the same as before, the difference is the Item data display:
- In the UserItemViewHolder, instead of findViewById, databindingutil.bind (view) is used. The ViewHolder simply holds the binding, instead of holding all views.
- In the UserListAdapter, set the data to yes by using the binding setUser(user).
Custom attribute – BindingAdapter
DataBinding has another powerful feature: a BindingAdapter that provides custom properties for controls!
Don’t understand? Let’s look at an example.
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
app:imageUrl="@{user.avatar}"
app:placeHolder="@{@drawable/dog}"/>
Copy the code
App :imageUrl; app:placeHolder; @drawable/dog. But we know that the ImageView itself does not have these two properties, and we are not a custom View that inherits from the ImageView, so why do we use it that way? Look at:
@BindingAdapter({"app:imageUrl", "app:placeHolder"})
public static void loadImageFromUri(ImageView imageView, String imageUri, Drawable placeHolder){
Glide.with(imageView.getContext())
.load(imageUri)
.placeholder(placeHolder)
.into(imageView);
}
Copy the code
Add public static method (name optional) to any class, add @bindingAdapter, and specify the corresponding “app:imageUrl”, “app:placeHolder”, The method parameters are then the control type and the corresponding values for the two properties. And then you write the logic in the method, so you’re going to use Glide to load the user’s avatar, and the placeHolder is the placeHolder.
This completes the image loading!
The use is really quite concise, equivalent to directly customizing attributes. You can customize any properties you want.
In general, we can use @bindingAdapter to do some common logic inside the module. For example, the @bindingAdapter annotation method is only written once, so all the places where ImageView is used to load images can be directly bound to the XML attributes app:imageUrl and app:placeHolder.
Third, combined with LiveData
Another nice thing about DataBinding is that it can be used in conjunction with LiveData.
Originally when we use DataBinding, we define variable data in XML, we have to either inherit BaseObservable or use ObservableField, Also add the @bindable annotation and call notifyPropertyChanged(br.name). This is to notify the corresponding bound View to refresh when the user.setName(name) field changes.
In our last article, MVVM is data-driven using LiveData. The User that it is enveloping does not inherit BaseObservable. Don’t have to!
With the advent of LiveData, you can replace ObservableField and automate life cycle management.
Instead of making intrusive changes to the data entity class, use LiveData directly and also support DataBinding DataBinding!
Using DataBinding in conjunction with LiveData is simple:
- To use the LiveData object as the data binding source, you need to set up the LifecycleOwner
- XML, and use the LiveData in the ViewModel to bind the corresponding control
- Binding Sets the variable ViewModel
// Combine the ViewModel used by DataBinding
//1. To use the LiveData object as the data binding source, you need to set LifecycleOwner
binding.setLifecycleOwner(this);
ViewModelProvider viewModelProvider = new ViewModelProvider(this);
mUserViewModel = viewModelProvider.get(UserViewModel.class);
//3. Set the ViewModel variable
binding.setVm(mUserViewModel);
Copy the code
<! -- 2. Define ViewModel and bind -->
<variable
name="vm"
type="com.hfy.demo01.module.jetpack.databinding.UserViewModel" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.userLiveData.name}"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{vm.userLiveData.level}"/>
Copy the code
That’s ok. You’ll notice that we don’t need to get LivaData to observe(owner, observer) in the Activity. The DataBinding automatically generates code to do that for us, so you need to set LifecycleOwner.
That said, in the Jetpack MVVM introduced in the previous article, DataBinding is easy to use, if at all.
Iv. Additional description of Jetpack MVVM
With DataBinding covered, the focus of all Jetpack architecture components is over.
Here are some additions to Jetpack AAC and MVVM:
- First, the ViewModel and View responsibilities are separated. The ViewModel handles business logic, and the View only displays data and passes events
- 2. ViewModel does not reference View and Context
- 3. View observes data changes through LiveData, rather than pushing data directly to View
- In addition to business LiveData, ViewModel should also provide LiveData, indicating that data is loading, loading success, loading failure.
- Use SingleLiveEvent to pass event class messages: observers are notified only when setValue() or call() is explicitly called; Only one observer will be notified of the change.
- 6. It is recommended to use LiveData for communication between ViewModel and Repository, as there is a risk of memory leaks if callbacks are used between View and ViewModel. And the use of Transformations in the ViewModel. SwitchMap lifecycle information transfer to the Repository LiveData.
- Use LivaData instead of BaseObservable for DataBinding binding data
- 8. Try to define only one variable in the XML, that is the ViewModel corresponding to the page, and the control is directly bound to the LivaData field
- Nine, XML try not to use logical expressions, the logic in the ViewModel, control binding terminal data
Five, the summary
This article focuses on the recognition of DataBinding: DataBinding’s essence is “terminal DataBinding to View”, rather than “writing logic in XML”; Custom attribute BindingAdapter; Combined with the use of LiveData. DataBinding has a significant advantage in the Jetpack MVVM architecture. Finally, the notes and principles for the use of the Jetpack MVVM architecture are explained, which should be appreciated in the actual project.
This is the end of the Jetpack AAC series. This is chapter five. Each article is intended to introduce the content as clearly as possible, including a lot of understanding after their own use. In the process, I also read a lot of relevant excellent articles and learned different opinions. Although the whole series is after reading the source code, actual use, after reading other excellent articles output, but unavoidable errors and omissions, welcome to leave a comment and discuss.
If you think the article is good, want to receive the article push at the first time, welcome to follow my public account Hu Feiyang. If you have any questions or want to join the group, add my wechat entrance in the number, I can pull you into the group. On the road of technology learning, we move forward together!
The Demo address
References and Thanks:
DataBinding official documents
ViewModel and LiveData: A Call or a naysayer for Design Patterns?
Relearn Android: From misunderstood to flavorful Jetpack DataBinding!
DataBinding for MVVM traps
.
Your likes and comments are a great encouragement to me!
Welcome to follow my public account, wechat search Hu Feiyang, article updates can be the first time to receive.