当前位置:网站首页>When you really learn databinding, you will find "this thing is really fragrant"!
When you really learn databinding, you will find "this thing is really fragrant"!
2022-07-01 13:34:00 【InfoQ】
Preface
How to use DataBinding Well ?
// stay gradle Of android Lower join , And then click sync
android {
...
//android studio 4.0 following
dataBinding{
}
//android studio4.1 in the future
buildFeatures {
dataBinding true
}
}
Convert to data binding layoutDataBinding
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
layoutdatadata<data>
<import type="com.example...."/>
<variable
name="color"
type="java.lang.String" />
</data>
- data:Declare and import variables in the tag
- variable:Declare variables
- import:Import the required classes
class User() {
var name = "Taxze"
var age = 18
fun testOnclick() {
Log.d("onclick", "test")
}
}
<data>
<!-- <variable-->
<!-- name="user"-->
<!-- type="com.taxze.jetpack.databinding.User" />-->
<import type="com.taxze.jetpack.databinding.User" />
<variable
name="user"
type="User" />
</data>
@{}// Pseudo code , Do not directly CV
<TextView
...
android:text="@{user.name}"
/>
DataBindingUtilActivity Of setContentViewclass MainActivity : AppCompatActivity() {
private lateinit var mainBinding: ActivityMainBinding
private lateinit var mainUser: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
mainUser = User()
mainBinding.user = mainUser
}
}
class BlankFragment : Fragment() {
private lateinit var mainFragmentBinding:FragmentBlankBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
mainFragmentBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_blank,container,false)
return mainFragmentBinding.root
}
}
Bindingactivity_mainActivityMainBinding
layoutdatabindingbindingbuild/generated/data_binding_base_source_out
How to be in xml Better use in layout DataBinding
- Join us and pass in a collection
books, We can use it in the following ways :
- Get the value of the collection
- Add default ( The default value does not need to be quoted , And only in the preview view )
- ```xml
android:text="@{books.pages,default=330}"
- adopt ?? or ?: To achieve
- ```xml
android:text="@{books.pages != null ? book.pages : book.defaultPages}"
- ```xml
android:text="@{books.pages ?? book.defaultPages}"
mapThe structure of type can also be through get and [] There are two ways to get
**3. Convert data type **
- because `DataBinding` No automatic type conversion , So we need to switch manually , For example, in text Use in label String.valueOf() Convert to String type , stay rating We can use Float.valueOf() convert
- ```xml
android:text="@{String.valueOf(book.pages)}"
- ```xml
android:rating="@{Float.valueOf(books.rating),default=2.0}"
**4. Import package name conflict handling **
- If the package names we import conflict , We can go through `alias` Set an alias for it
- ```xml
// Pseudo code , Do not directly CV
<data>
<import type="com.xxx.a.Book" alias="aBook"/>
<import type="com.xxx.B.Book" alias="bBook"/>
...
</data>
- In a
viewQuote otherviewProperties of
- **`include`** Labels and **`ViewStub`** label
- `include` and `merge` The function of tags is to realize the reuse of layout files . That is to say , In order to reuse and integrate layout efficiently , Make the layout portable , We can use `include` and `merge` Tags embed one layout into another , Or extract the same elements from multiple layouts , Independent management , And then reuse it in various layouts , Convenient for unified adjustment . such as , Multiple pages in an application need to use a unified style title bar or bottom navigation bar , At this time, you can extract the layout of the title bar or the bottom navigation bar , And then to `include` The label form is embedded in the required layout , Not many times copy Code , In this way, you only need to modify one place when modifying . And we can also pass `DataBinding` To do data binding .
- for example :
- ```xml
//layout_title.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.xxx.User" />
<variable
name="userInfo"
type="User" />
</data>
<android.support.constraint.ConstraintLayout
...
>
<TextView
...
android:text="@{userInfo.name}" />
</android.support.constraint.ConstraintLayout>
</layout>
- Use this layout , And pass the value
- ```
<include
layout="@layout/layout_title"
bind:test="@{userInfo}"/>
- ViewStub It's a similar use , Not here .
- DataBinding I won't support it merge label
**6. Bind click event **
- ```xml
// Pseudo code , Do not directly CV
onclick="@{()->user.testOnclick}"
onclick="@{(v)->user.testOnclick(v)}"
onclick="@{()->user.testOnclick(context)}"
onclick="@{BindHelp::staticClick}"
onclick="@{callback}"
// for example :
<Button
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="@{()->user.testOnclick}"
/>
Realize automatic update when data changes UI
BaseObservableObservableFieldObservableCollection- BaseObservable
- BaseObservable Two refreshes are provided UI Methods , Namely notifyPropertyChanged() and notifyChange() .
- First step : Modify entity class
- Inherit our entity class from BaseObservable. Fields that need to respond to changes , In the corresponding variable get Add... To the function @Bindable . then set in notifyChange yes kotlin Writing , Save it. java Of getter setter The way . Member attributes need to respond to changes , It's in it set Function ,notify Let's talk about attribute changes , that set When ,databinding You'll feel .
import androidx.databinding.BaseObservable
import androidx.databinding.Bindable
import androidx.databinding.library.baseAdapters.BR
class User() : BaseObservable() {
constructor(name: String, age: Int) : this() {
this.name = name
this.age = age
}
// This is alone in set On @bindable,name Can be a declaration private
var name: String = ""
set(value) {
field = value
notifyPropertyChanged(BR.name)
}
@Bindable
get() = field
// This is declared on the entire variable @bindable, So it has to be public Of
@Bindable
var age:Int = 18
set(value) {
field = value
notifyPropertyChanged(BR.age)
}
get() = field
}
- The second step : stay `Activity` Use in
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
private lateinit var mainBinding: ActivityMainBinding
private lateinit var mainUser: User
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainBinding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
mainUser = User("Taxze", 18)
mainUser.addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable, propertyId: Int) {
when {
BR.user == propertyId -> {
Log.d(TAG, "BR.user")
}
BR.age == propertyId -> {
Log.d(TAG, "BR.age")
}
}
}
})
mainBinding.user = mainUser
mainBinding.onClickPresenter = OnClickPresenter()
}
inner class OnClickPresenter {
fun changeName() {
mainUser.name = "Taxze2222222"
}
}
}
- Something to watch out for
- The official website just prompts to open `DataBinding` Only need `build.gradle` Add the following line of code to the
- ```
buildFeatures {
dataBinding true
}
however , If you want to use better `DataBinding` This is not enough , You also need to add these configurations :
- ```
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
- a key : In the use of `DataBinding` It's time ,`BR` object , I found that I couldn't call , The generation will also report an error , function , We need to `build.gradle` Configure in :
apply plugin: 'kotlin-kapt'
kapt {
generateStubs = true
}
Then run the code again , You'll find out ,`BR` The file is automatically generated !
- ObservableField
- Explained BaseObservable after , Now let's build the simplest and most commonly used . Just change the entity class to this :
// Be careful observable The properties of require public jurisdiction , otherwise dataBinding You cannot process data responses through reflection
class User() : BaseObservable() {
var name: ObservableField<String> = ObservableField("Taxze")
var age:ObservableInt = ObservableInt(18)
}
- ObservableCollection
- dataBinding Wrapper classes are also provided to replace native List and Map, Namely ObservableList and ObservableMap
- Entity class modification :
// Pseudo code , Do not directly cv
class User(){
var userMap = ObservableArrayMap<String,String>()
}
// When using :
mainUser.userMap["name"] = "Taxze"
mainUser.userMap["age"] = "18"
Use `ObservableCollection` after ,`xml` Slightly different from the above , Mainly data acquisition , You need to specify the `Key` value
// Pseudo code , Do not directly cv
...
<import type="android.databinding.ObservableMap" />
<variable
name="userMap"
type="ObservableMap<String, String>" />
// When using :
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="Name"
android:text="@{userMap[`userName`]}" />
- It only needs to be based on the previous one-way binding , Add layout file to
@{}Turn into@={}, It is used to monitor the user's update when the data of the attribute changes
DataBinding stay RecyclerView The use of

- First step : Create entity class
- That's what we had before , Used BaseObservable The entity class of , No more code here
- The second step : establish activity_main To hold recyclerview
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activty_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</RelativeLayout>
- The third step : establish text_item.xml For display recyclerview Every row of data in
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<import type="com.taxze.jetpack.databinding.User" />
<variable
name="user"
type="User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#ffffff"
android:orientation="horizontal"
android:paddingStart="10dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@{` This person's name is ` + user.name}" />
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="20dp"
android:text="@{String.valueOf(user.age)}" />
</LinearLayout>
</LinearLayout>
</layout>
- Step four : establish Adapter
- After having the foundation before , It should be easy to see the following code , Don't explain too much
import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
import com.taxze.jetpack.databinding.databinding.TextItemBinding
class FirstAdapter(users: MutableList<User>, context: Context) :
RecyclerView.Adapter<FirstAdapter.MyHolder>() {
// Declare... In the constructor binding Variable , such holder In order to quote , If not val/var, You can't quote , It needs to be in class Of {} Write in get function
class MyHolder(val binding: TextItemBinding) : RecyclerView.ViewHolder(binding.root)
private var users: MutableList<User> = arrayListOf()
private var context: Context
init {
this.users = users
this.context = context
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
val inflater = LayoutInflater.from(context)
val binding: TextItemBinding =
DataBindingUtil.inflate(inflater, R.layout.text_item, parent, false)
return MyHolder(binding)
}
override fun onBindViewHolder(holder: MyHolder, position: Int) {
//java It can be written in setVariable
holder.binding.user = users[position]
holder.binding.executePendingBindings()
}
//kotlin in ,return The way , It can be abbreviated
override fun getItemCount() = users.size
}
- Step five : stay MainActivity Use in
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
// to RecyclerView Set up the data
private fun initView() {
val recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
recyclerView.layoutManager = LinearLayoutManager(this)
val users: MutableList<User> = ArrayList()
for (i in 0..100) {
val user = User()
user.name = "Taxze"
user.age = i
users.add(user)
}
val adapter = FirstAdapter(users, this)
recyclerView.adapter = adapter
}
}
Advanced usage
// Pseudo code , Do not directly cv
/**
* be used for appCompatImageView Custom properties for ,bind:imgSrc, Namespace bind: It can be omitted , That's writing imgSrc Yes . Can be used to load url Pictures of the
* The function name is also arbitrary , Mainly value Statement of , Is the newly added attribute name , Multiple attributes can be used together , And configure whether it must work together
* The function name is arbitrary , Method signature is important , Match object control , And attribute parameters .
* You can also add old Parameters , Get and modify new parameters The previous corresponding value .
* todo Load network pictures , Network permissions are required !!!
*/
@JvmStatic
@BindingAdapter(value = ["bind:imgSrc"], requireAll = false)
fun urlImageSrc(view: AppCompatImageView, /*old: String?, */url: String) {
Glide.with(view)
.load(url)
.placeholder(R.drawable.img_banner)
.centerInside()
.into(view)
}
- First step : One way , Data changes , Refresh UI
// Pseudo code , Do not directly cv
@JvmStatic
@BindingAdapter("sfl_refreshing", requireAll = false)
fun setSwipeRefreshing(view: SwipeRefreshLayout, oldValue: Boolean, newValue: Boolean) {
// Determine whether it is a new value , Avoid falling into a dead cycle
if (oldValue != newValue)
view.isRefreshing = newValue
}
- The second step :ui The state of , Reverse bind to data changes
// Pseudo code , Do not directly cv
@JvmStatic
@BindingAdapter("sfl_refreshingAttrChanged", requireAll = false)
fun setRefreshCallback(view: SwipeRefreshLayout, listener: InverseBindingListener?) {
listener ?: return
view.setOnRefreshListener {
// from ui The refresh state of the layer changes , Reverse notification of changes in the data layer
listener.onChange()
}
}
- The third step : Implementation of reverse binding
// Pseudo code , Do not directly cv
/**
* Implementation of reverse binding , take UI The change of , Call back to bindingListener,listener will onChange, Notification of data changes
* Notice the attribute and event, It is effective only if it is consistent with the above two steps
*/
@JvmStatic
@InverseBindingAdapter(attribute = "sfl_refreshing", event = "sfl_refreshingAttrChanged")
fun isSwipeRefreshing(view: SwipeRefreshLayout): Boolean {
return view.isRefreshing
}
DataBinding coordination ViewModel&LiveData Use it together

- First step : establish UserModel
// Inherit it from AndroidViewModel(AndroidViewModel It is also inherited from ViewModel Of , however ViewModel There is no way to get Context,AndroidViewModel Provide Application Used as a Context, And specially provide Application Single case )
//UserName Use MutableLiveData
class UserModel(application: Application) : AndroidViewModel(application) {
var UserName = MutableLiveData("")
}
- The second step : establish activity_main And corresponding MainActivity
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="loginModel"
type="com.taxze.jetpack.databinding.model.UserModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/linearLayout"
style="@style/InputBoxStyle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="17dp"
android:layout_marginEnd="17dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints">
<EditText
android:id="@+id/editText"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_height="50dp"
android:hint=" Please enter your account number "
android:text="@={loginModel.UserName}"
tools:ignore="MissingConstraints" />
</LinearLayout>
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text='@{" The account name you entered is :"+loginModel.UserName,default=123123123}'
android:textSize="24sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/button"
tools:ignore="MissingConstraints" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.4" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:background="@drawable/button_drawable"
android:text=" Sign in "
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linearLayout"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
- The third step : stay MainActivity Binding page and binding declaration cycle
class MainActivity : AppCompatActivity() {
lateinit var viewDataBinding: ActivityMainBinding
lateinit var model: UserModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Binding page
viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
// Binding lifecycle
viewDataBinding.lifecycleOwner = this
model = ViewModelProvider.AndroidViewModelFactory.getInstance(this.application)
.create(UserModel::class.java)
viewDataBinding.loginModel = model
...
}
}
- Step four : Pass value
viewDataBinding.button.setOnClickListener {
val intent = Intent([email protected] this, SecondActivity::class.java)
intent.putExtra("user", "${model.UserName.value}")
startActivity(
intent
)
}
- Step five : On the other side activity Call in
class SecondActivity : AppCompatActivity() {
lateinit var viewDataBinding: ActivitySecondBinding
lateinit var userName: String
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Binding page
viewDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_second)
// Binding lifecycle
viewDataBinding.lifecycleOwner = this
userName = intent.getStringExtra("user").toString()
viewDataBinding.tvName.text = " The login account is :$userName"
}
}
Help you step on the pit :
TextViewOftextattribute , We need to pay attention to data Not forNumbertype
xmlChinese characters cannot be Chinese ( Check your input method )
- Reflection attribute 、 The function must be
public
observableFieldData time , In some cases, initialization is required , Otherwise, an error will be reported !
- Use
LiveDataAsdataBindingWhen , Need to be inuiSet inbinding.lifecycleOwner
Epilogue
About me
边栏推荐
- MySQL statistical bill information (Part 2): data import and query
- 面试题目总结(1) https中间人攻击,ConcurrentHashMap的原理 ,serialVersionUID常量,redis单线程,
- Have you ever encountered the problem that flynk monitors the PostgreSQL database and checkpoints cannot be used
- Detailed explanation of parallel replication examples in MySQL replication
- 3.4 《数据库系统概论》之数据查询—SELECT(单表查询、连接查询、嵌套查询、集合查询、多表查询)
- Blind box NFT digital collection platform system development (build source code)
- Listen in the network
- What is the future development direction of people with ordinary education, appearance and family background? The career planning after 00 has been made clear
- Content Audit Technology
- Hardware development notes (9): basic process of hardware development, making a USB to RS232 module (8): create asm1117-3.3v package library and associate principle graphic devices
猜你喜欢

【大型电商项目开发】性能压测-压力测试基本概念&JMeter-38
基于mysql乐观锁实现秒杀的示例代码

5. Use of ly tab plug-in of header component
![[development of large e-commerce projects] performance pressure test - basic concept of pressure test & jmeter-38](/img/50/819b9c2f69534afc6dc391c9de5f05.png)
[development of large e-commerce projects] performance pressure test - basic concept of pressure test & jmeter-38

MySQL statistical bill information (Part 2): data import and query

1553B环境搭建

介绍一种对 SAP GUI 里的收藏夹事务码管理工具增强的实现方案

SAP intelligent robot process automation (IRPA) solution sharing

minimum spanning tree

Application of 5g industrial gateway in scientific and technological overload control; off-site joint law enforcement for over limit, overweight and overspeed
随机推荐
Camp division of common PLC programming software
MySQL Replication中的并行复制示例详解
Analysis report on the development prospect and investment strategy of the global and Chinese laser chip industry Ⓑ 2022 ~ 2027
Several models of IO blocking, non blocking, IO multiplexing, signal driven and asynchronous IO
Wave animation color five pointed star loader loading JS special effects
学会使用LiveData和ViewModel,我相信会让你在写业务时变得轻松
【机器学习】VAE变分自编码器学习笔记
关于佛萨奇2.0“Meta Force原力元宇宙系统开发逻辑方案(详情)
JS变色的乐高积木
spark源码(五)DAGScheduler TaskScheduler如何配合提交任务,application、job、stage、taskset、task对应关系是什么?
Analysis report on the development prospect and investment strategic planning of China's wafer manufacturing Ⓔ 2022 ~ 2028
leetcode 322. Coin change (medium)
介绍一种对 SAP GUI 里的收藏夹事务码管理工具增强的实现方案
[machine learning] VAE variational self encoder learning notes
Report on the current situation and development trend of bidirectional polypropylene composite film industry in the world and China Ⓟ 2022 ~ 2028
Cs5268 advantages replace ag9321mcq typec multi in one docking station scheme
Global and Chinese n-butanol acetic acid market development trend and prospect forecast report Ⓧ 2022 ~ 2028
leetcode 322. Coin Change 零钱兑换(中等)
基于mysql乐观锁实现秒杀的示例代码
ZABBIX 6.0 source code installation and ha configuration