ViewBinding and Databinding
What is a ViewBinding
Viewbinding is a feature of Android Jetpack that makes it easier to write code that interacts with views. After viewBinding is enabled in a module, a binding class is generated for each XML layout file in that module. Instances of the binding class contain direct references to all views that have ids in the corresponding layout.
usage
How to configure Viewbinding
Do the following configuration in build.gradle for each module
android {
...
viewBinding {
enabled = true}}Copy the code
When view binding is enabled for a module, a binding class is generated for each XML layout file contained in that module. Each binding class contains references to the root view as well as all views with ids. The system generates the name of the Binding class by converting the name of the XML file to camel case and adding the word “Binding” to the end.
Viewbinding is used in an Activity
Example: Suppose you have a layout called activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context=".MainActivity">
<Button
android:text="Here's the button."
android:id="@+id/test_view_binding"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Copy the code
In an Activity, viewBinding is used as follows:
class MainActivity : Activity() {
// Declare variables first
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
// Load the layout with a generated binding
binding = ActivityMainBinding.inflate(layoutInflater)
// Call getRoot() of the Binding class to get an instance of the root element in activity_main.xml
val view = binding.root
// Pass the root view to setContentView() to make it the active view on the screen
setContentView(view)
// Use the Binding instance to get the control
binding.testViewBinding.text = "button"}}Copy the code
Viewbinding is used in fragments
Using a viewBinding on the Fragment, here is an example from the website
private var _binding: ResultProfileBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup? , savedInstanceState:Bundle?).: View? {
_binding = ResultProfileBinding.inflate(inflater, container, false)
val view = binding.root
return view
}
override fun onDestroyView(a) {
super.onDestroyView()
_binding = null
}
Copy the code
And here’s why it’s a little bit weird to implement. First of all, kotlin is null safe, so using _binding directly is not possible. Why not declare the LateInit variable as in activity? This binding variable is only available in onCreateView and onDestroyView. Because our fragment life cycle is different from that of an activity, a fragment can extend beyond the life of its view and can cause a memory leak if it is not left empty.
Viewbinding contains the use of layouts in include tags
title_bar.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="include"
android:id="@+id/test_include"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
Copy the code
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
tools:context=".MainActivity">
<! Add id to include tag -->
<include
android:id="@+id/title_bar"
layout="@layout/title_bar"/>.</LinearLayout>
Copy the code
Use a layout that includes include tags in your activity
class MainActivity : Activity() {
// Declare variables first
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?).{...// Use the id of the include tag directly, then reference the id of the include layout according to the ID of the include
binding.titleBar.testInclude.text = "hello"}}Copy the code
Viewbinding is used in the layout of labels that contain merge
The first step is to remove the id from the include tag, otherwise an error will be reported directly.
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<Button
android:text="include"
android:id="@+id/test_include"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
Copy the code
Next comes the use in activities
private lateinit var binding: ActivityMainBinding
// Declare variables
private lateinit var titleBarBinding: TitleBarBinding
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
// Call TitleBarBind's bind function to associate title_bar. XML with our activity_main.xml
titleBarBinding = TitleBarBinding.bind(binding.root)
val view = binding.root
setContentView(view)
// Reference the control directly with the titlrBarBinding variable
titleBarBinding.testInclude.text = "button"
}
Copy the code
Viewbinding contains the use in Adapter
class BindingAdapter(val mData:List<String>): RecyclerView.Adapter<BindingAdapter.MyHolder>() {
//Myholder takes a RvItemBinding, recyclerView. ViewHolder takes a View, and returns a root from this binding
inner class MyHolder(binding: RvItemBinding):RecyclerView.ViewHolder(binding.root){
val textView = binding.textView
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
// First call the RvItemBinding inflate function to load the rv_item.xml layout
val binding = RvItemBinding.inflate(LayoutInflater.from(parent.context),parent,false)
return MyHolder(binding)
}
override fun getItemCount(a) = mData.size
override fun onBindViewHolder(holder: MyHolder, position: Int) {
// Use its own member variables directly through the holder
holder.textView.text = mData[position]
}
}
Copy the code
What can we benefit from using viewBinding
The benefits of using viewBinding over traditional findViewById
1. Type safety: Don’t worry about casting errors
2. Easy to write, do not have to write a lot of declaration code, making the code inside the Activity more clean
Using viewBinding has advantages over synthetic
The Synthetic algorithm is implemented by a plug-in provided by Android. This can be referenced by adding the apply plugin: ‘kotlin-android-extensions’ to build.gradle.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_synthetic.*
class SyntheticActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_synthetic)
// Use synthetic_button directly to use the control
synthetic_button.text = "hello"}}Copy the code
But this plugin is no longer recommended.
If this plugin is so useful, why is it not recommended by Google? Google doesn’t say that either. Let’s decompile the above code directly into Java code. You get the following code
package com.eebbk.mvvmlearn;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.eebbk.mvvmlearn.R.id;
import java.util.HashMap;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.Nullable;
public final class SyntheticActivity extends AppCompatActivity {
// Add a member variable
private HashMap _$_findViewCache;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(1300109);
Button var10000 = (Button)this._$_findCachedViewById(id.synthetic_button);
Intrinsics.checkExpressionValueIsNotNull(var10000, "synthetic_button");
var10000.setText((CharSequence)"hello");
}
// Avoid duplicates with user-declared functions by naming a strange function and finding our view through this function
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View)this._$_findViewCache.get(var1);
if (var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(var1, var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if (this._$_findViewCache ! =null) {
this._$_findViewCache.clear(); }}}Copy the code
You can see that we’re adding a new member variable to help us implement findViewById. This virtually increases our memory overhead. That’s one of the things, and one of the things is that it increases the instability of our program. Colleagues who have used this control will know.
He by introducing import kotlinx. Android. Synthetic. Main. Activity_synthetic. * to directly use control id use control. The problem is that if you accidentally introduce other layouts and use controls for other layouts, the error will not be detected at compile time. Is a runtime error. This runtime error makes our program unstable. Especially once the project is complicated, there are many controls with the same name, which will increase the instability of our program.
dataBinding
What is the databinding
Data binding: A data binding library is a support library that allows you to bind interface components in a layout to data sources in an application using declarative personalities rather than programmatically.
We can bind the value of a property of a control in a layout file to a value in our program.
usage
How do I configure databinding
To use databinding in your project, you need to do the following configuration in the build.gradle module
android {
...
dataBinding {
enabled = true}}Copy the code
introductory
First the layout file is modified to make certain changes. The layout file has layout as the root tag. Where the data tag is our data element, followed by our view element.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<! -- Data elements -->
<data>
<variable
name="user"
type="com.eebbk.mvvmlearn.bean.User" />
</data>
<! -- View element -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/user_name"
android:text="@{user.userName}"
android:layout_width="200px"
android:layout_height="70px"/>
</LinearLayout>
</layout>
Copy the code
You can use shortcuts to quickly create a Databinding layout. Use Alt+Enter in the layout file, then pop up the following window, and click Convert to Data Binding Layout to quickly Convert.
data class User(var userName:String = "") {}Copy the code
class DataActivity : AppCompatActivity() {
lateinit var dataBindingActivity:ActivityDataBinding
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
// Bind the layout to the activity using DataBindingUtil
dataBindingActivity = DataBindingUtil.setContentView(this,R.layout.activity_data)
val user = User("hello")
// Assign values to the data elements in the layout file
dataBindingActivity.user = user
//user.userName = "hello world"}}Copy the code
So that’s the basic use of databinding. If I change the userName property of the user variable, does that change the property value of the control? The answer is: no.
So what do we do if we want the property value of the control to change? Let’s talk about one-way data binding.
One-way data binding
BaseObservable
You want the UI to refresh immediately after data changes, so you need an Observable to do this. Let’s walk through this one-way data binding step by step with an example.
// Our entity class extends from BaseObservable
class User():BaseObservable() {
@get:Bindable
var userName:String = ""
set(value) {
field = value
//BR is a class generated at compile time. It has a similar function to R.Java. NotifyPropertyChanged updates the view associated with this property. This step must be carried out.
notifyPropertyChanged(BR.userName)
}
}
Copy the code
BaseObservable provides two methods, one notifyPropertyChanged(int fieldId) and the other notifyChange()
NotifyPropertyChanged (int fieldId) Refreshes one property of our entity class. NotifyChange () refreshes all properties of our entity class.
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.eebbk.mvvmlearn.bean.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/user_name"
android:text="@{user.userName}"
android:layout_width="200px"
android:layout_height="70px"/>
<Button
android:text="Change your name"
android:id="@+id/change_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</layout>
Copy the code
class DataActivity : AppCompatActivity() {
lateinit var dataBindingActivity:ActivityDataBinding
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
dataBindingActivity = DataBindingUtil.setContentView(this,R.layout.activity_data)
val user = User()
user.userName = "hello"
dataBindingActivity.user = user
dataBindingActivity.changeName.setOnClickListener{
// By resetting the userName of the user, the UI will refresh in time.
user.userName = "hello world"}}}Copy the code
ObservableField
The above is how to use a BaseObservable, which is quite complicated. So we also have an ObservableField class. This class is also based on a BaseObservable wrapper, but its usage is simpler.
class User2 {
var userName:ObservableField<String> = ObservableField("")}Copy the code
. dataBindingActivity.changeName.setOnClickListener{// The set function is used to change the value of userName. User2.username = ObservableField("") cannot update the UI because the ObservableField notifyChange() is executed only when the set function is executed.
user2.userName.set(Hello world.)}Copy the code
Two-way binding
One-way binding is when the data changes and the bound UI view is refreshed. Does our data change when the data in the UI changes? The answer must be yes. So how do we implement this data change in our view, and our application’s data change as well? This is where bidirectional binding is needed. Android :text=”@{user.username}” Android :text=”@={user.username}” android:text=”@={user.username}” android:text=”@={user.username}” Of course, there will be LiveData data structures in JetPack that will make it easier to implement Databinding.
event
Event binding is setting variable binding to the callback interface. Commonly used for event binding are
- The onClick,
- OnLongClick,
- AfterTextChanged,
- OnTextChanged, etc.
Here is an example of a login
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="userInfo"
type="com.eebbk.mvvmlearn.bean.UserInfo" />
<variable
name="loginPresenter"
type="com.eebbk.mvvmlearn.LoginPresenter" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:afterTextChanged = "@{loginPresenter.setLoginName}"
android:id="@+id/login_name"
android:layout_width="match_parent"
android:layout_height="100px"/>
<EditText
android:afterTextChanged = "@{loginPresenter.setLoginPassword}"
android:id="@+id/login_password"
android:layout_width="match_parent"
android:layout_height="100px"/>
<Button
android:onClick="@{()->loginPresenter.login(userInfo)}"
android:text="Login"
android:id="@+id/login_button"
android:layout_width="match_parent"
android:layout_height="100px"/>
</LinearLayout>
</layout>
Copy the code
class LoginPresenter(private val userInfo: UserInfo,
private val binding: ActivityLoginBinding) {
// Handle the login callback, click login to callback here
fun login(userInfo: UserInfo){
Log.d("hch",userInfo.toString())
}
// Handle the callback, which is called when the user name changes
fun setLoginName(loginName:Editable){
userInfo.loginName = loginName.toString()
binding.userInfo = userInfo
}
// Handle the callback, which is called when the password changes
fun setLoginPassword(loginPassword:Editable){
userInfo.loginPassword = loginPassword.toString()
binding.userInfo = userInfo
}
}
Copy the code
This is the event binding, of course, this login example can also be implemented by bidirectional binding, and it is much easier to implement.
Reference: guolin.blog.csdn.net/article/det…