DataBinding介绍
- Android开发JetPack-Databinding组件
- 1. 什么是databinding
- (1)简介
- (2)意义
- 2. databinding基本使用
- (1)启用databinding
- (2)定义一个布局
- (3)创建一个User类
- (4)改变MainActivity.kt的代码
- 3. 通过DatabindingUtil获取到Binding
- 4.给BaseViewFragment加载View
- 5.编写BaseVmFragment
- 6.布局和绑定表达式
- (1)其他控件引用,TextView引用EditText。
- (2)表达式中也可以引用资源内容。
- (3)某些资源需要用到特定的类型。
- 7.事件处理
- 事件处理,一种为方法引用,一种为监听绑定。
- 实例
- 8.双向绑定
- 双向绑定要达到的效果便是除了数据影响界面,界面变化也要使得数据发生变化。比如EditText输入内容时,绑定的数据bean要跟着变化。
- 9.数据更新-->UI更新
- Databinding通过使用实现Observable的数据,当数据更新的时候,自动更新UI。
- 监听对象变化更新
- 10.BindingAdapter
- DataBinding支持在普通方法上添加@注解来添加自定义控件属性。
- 11.小结
Android开发JetPack-Databinding组件
1. 什么是databinding
(1)简介
Databinding是谷歌的一个官方支持库,它允许您使用声明性格式而不是通过编程方式将布局中的UI组件绑定到应用程序中的数据源。通常在活动中使用调用UI框架方法的代码来定义布局。例如,调用findViewById()以查找TextView窗口小部件并将其绑定到变量。因为它通过在布局文件中绑定组件,您可以删除活动中的许多UI框架调用,从而使它们更易于维护。这也可以提高应用程序的性能,并有助于防止内存泄漏和空指针异常。
(2)意义
1、布局文件通常只负责UI控件的布局工作,页面中通过代码对控件需要进行各种操作,承担了绝大部分的工作量
2、DataBinding让布局文件承担了部分原本属于页面的工作,也使得布局文件和页面的耦合度进一步降低
3、使得UI控件能够直接合数据模型中的字段绑定,甚至能响应用户的交互。方便实现MVVM
2. databinding基本使用
(1)启用databinding
在模块下的build.gradle文件中,启动dataBinding。
android {
...
dataBinding {
enabled = true
}
}
(2)定义一个布局
显示用户的姓名,年龄和性别,将普通布局文件转换为DataBinding布局文件。
转换后代码如下:
<?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="user"
type="com.example.myapplication.domain.User" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.10259918" />
<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.2" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_name"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_age"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_gender"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toStartOf="@+id/guideline4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<TextView
android:id="@+id/textView4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.name}"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{user.gender.toString()}"
app:layout_constraintBottom_toTopOf="@+id/guideline3"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline2" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{String.valueOf(user.age)}"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline4"
app:layout_constraintTop_toTopOf="@+id/guideline" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(3)创建一个User类
封装用户数据。
package com.sunofbeaches.databindingdemo.domain
data class User(var name: String, var age: Int, var gender: Gender)
enum class Gender {
FEMALE, MALE
}
(4)改变MainActivity.kt的代码
拿到布局并给User赋值。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//如何获取到Binding,获取binding后可以给其中的变量赋值
val binding:ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
//获取到View
binding.user = User("LiMing", 30, Gender.Male)
setContentView(binding.root)
}
}
3. 通过DatabindingUtil获取到Binding
(1)DataBindingUtil返回T,T是一个泛型,将其设为ActivityMainBinding,传参activity和Resource.id。
(2)ActivityMainBinding继承ViewDataBinding,返回binding。
(3)不需要设置ContentView,因为已经设置。
代码:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//把布局交给DataBindingUtil
val activityMainBinding: ActivityMainBinding =
DataBindingUtil.setContentView(this, R.layout.activity_main)
//设置数据
activityMainBinding.user = User("LiMing", 20, Gender.MALE)
}
}
4.给BaseViewFragment加载View
创建一个base包,创建BaseViewFragment类继承Fragmen,创建方法返回View。
abstract class BaseViewFragment<T : ViewDataBinding> : Fragment() {
protected var binding: T? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate<T>(inflater, getSubLayoutId(), container, false)
return binding!!.root
}
abstract fun getSubLayoutId() : Int
}
5.编写BaseVmFragment
创建BaseVmFragment类。
abstract class BaseVmFragment<T : ViewDataBinding,VM:ViewModel> : BaseViewFragment<T>() {
protected lateinit var viewModel: VM
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//创建ViewModel
initViewModel()
//观察数据变化--->更新UI
abserverData()
//设置相关的事件
initEvent()
}
open fun initEvent(){
}
open fun abserverData(){
}
//创建ViewModel
private fun initViewModel() {
viewModel = ViewModelProvider(this).get(getSubVMClass())
}
abstract fun getSubVMClass(): Class<VM>
}
6.布局和绑定表达式
(1)其他控件引用,TextView引用EditText。
<?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">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.3" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.1" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.2" />
<TextView
android:id="@+id/textView9"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_account"
app:layout_constraintBottom_toTopOf="@+id/guideline6"
app:layout_constraintEnd_toStartOf="@+id/guideline5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView10"
android:text="@{inputBox.text}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/guideline7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="@+id/guideline6" />
<EditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/input_box"
android:hint="请输入用户名"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/guideline6"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline5"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
(2)表达式中也可以引用资源内容。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="user"
type="com.sunofbeaches.databindingdemo.domain.User" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:textColor="@{@color/colorAccent}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/app_name}" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{@string/text_user_info(user.name,user.age)}" />
</LinearLayout>
</layout>
class ResourcesActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val resourcesBinding = DataBindingUtil.setContentView<ActivityResourcesBinding>(
this,
R.layout.activity_resources
)
resourcesBinding.user = User("LiMing", 20, Gender.MALE)
}
}
(3)某些资源需要用到特定的类型。
7.事件处理
事件处理,一种为方法引用,一种为监听绑定。
首先需要修改数据的内容。
<data class="EventBinding">
<variable
name="textInfo"
type="String" />
<variable
name="eventHandler"
type="com.sunofbeaches.databindingdemo.EventHandlers" />
</data>
实例
修改MainActivity的内容。
class EventActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val eventBinding =
DataBindingUtil.setContentView<EventBinding>(this, R.layout.activity_event)
eventBinding.textInfo = "点击复制内容"
eventBinding.eventHandler = EventHandlers()
}
}
方法处理类。
class EventHandlers {
fun onFistButtonClick(view: View) {
println("第一个按钮点击了...")
}
fun onTextClickToCopy(text: String) {
println("复制的内容是:$text")
}
}
方法引用
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{eventHandler::onFistButtonClick}"
android:text="第一个按钮点击" />
监听绑定
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="@{()->eventHandler.onTextClickToCopy(textInfo)}"
android:text="@{textInfo}" />
<CheckBox
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onCheckedChanged="@{(cb, isChecked) -> eventHandler.completeChanged(cb, isChecked)}"
android:text="选择测试" />
8.双向绑定
双向绑定要达到的效果便是除了数据影响界面,界面变化也要使得数据发生变化。比如EditText输入内容时,绑定的数据bean要跟着变化。
布局文件
<EditText
<?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>
<import type="com.example.myapplication.DataBean" />
<variable
name="databean"
type="DataBean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_marginTop="30dp"
android:gravity="center|left"
android:paddingLeft="20dp"
android:text="Data数据:"
android:textSize="13dp"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="2dp"
android:text="@{databean.data}"
android:background="#03A9F4"
android:gravity="left|center"
android:paddingLeft="20dp"
android:textColor="#FFFFFF"
android:textSize="14dp"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:ignore="MissingConstraints"
tools:layout_editor_absoluteX="0dp" />
<EditText
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginTop="15dp"
android:background="#F2F2F2"
android:text="@={databean.data}"
android:hint="请输入数据"
android:paddingLeft="20dp"
android:textSize="14dp"
app:layout_constraintTop_toBottomOf="@+id/textView2"
tools:layout_editor_absoluteX="0dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity中的代码
package com.example.myapplication;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableField;
import com.example.myapplication.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding activityMainBinding;
private DataBean dataBean;
@SuppressLint({"SetTextI18n", "InlinedApi"})
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
dataBean = new DataBean(new ObservableField<String>(""));
activityMainBinding.setDatabean(dataBean);
}
}
DataBean中的代码
package com.example.myapplication;
import androidx.databinding.ObservableField;
public class DataBean {
public final ObservableField<String> data;
public DataBean(ObservableField<String> data) {
this.data = data;
}
public ObservableField<String> getData() {
return data;
}
}
9.数据更新–>UI更新
Databinding通过使用实现Observable的数据,当数据更新的时候,自动更新UI。
class PlainObjectActivity : AppCompatActivity() {
private lateinit var dataBinding: ActivitySimpleObserableBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding = DataBindingUtil.setContentView<ActivitySimpleObserableBinding>(
this,
R.layout.activity_simple_obserable
)
dataBinding.activity = this
dataBinding.simpleUser = SimpleUser().apply {
age.set(20)
userName.set("Lisa")
}
}
fun onButtonClick() {
Toast.makeText(this, "更新数据...", Toast.LENGTH_SHORT).show()
dataBinding.simpleUser?.apply {
age.set(18)
userName.set("LiHua")
}
}
}
监听对象变化更新
常用的Observable
-
ObservableBoolean
-
ObservableByte
-
ObservableChar
-
ObservableShort
-
ObservableInt
-
ObservableLong
-
ObservableFloat
-
ObservableDouble
-
ObservableParcelable
-
ObservableArrayMap
-
ObservableArrayList
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) dataBinding = DataBindingUtil.setContentView<ActivitySimpleObserableBinding>( this, R.layout.activity_simple_obserable ) dataBinding.activity = this dataBinding.observableUser = ObservableObjectUser().apply { age = 20 userName = "Lisa" } } fun onButtonClick() { Toast.makeText(this, "更新数据...", Toast.LENGTH_SHORT).show() dataBinding.observableUser?.apply { age = 18 userName = "LiHua" } } }
10.BindingAdapter
DataBinding支持在普通方法上添加@注解来添加自定义控件属性。
(1)修饰方法, 要求方法必须public static
(2)方法参数第一个要求必须是View
(3)方法名不作要求
使用方法如下:
@BindingMethods({
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:indeterminateTint", method = "setIndeterminateTintList"),
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:progressTint", method = "setProgressTintList"),
@BindingMethod(type = android.widget.ProgressBar.class, attribute = "android:secondaryProgressTint", method = "setSecondaryProgressTintList"),
})
public class ProgressBarBindingAdapter {
}
11.小结
databinding用来实现 vm层和v层的双向绑定关系;取代繁琐的findViewByid();绑定VM层和V层的关联关系,实现双向交互;
作者:李思雨
原文地址:https://blog.csdn.net/m0_54156164/article/details/128105290?spm=1001.2014.3001.5502