目录
一、DataBinding简介
DataBinding 是一种用于在安卓应用中实现简洁、高效的数据绑定的库。它使开发者能够以声明性的方式将 UI 组件与数据源进行绑定,从而自动更新界面上的数据,并简化了与 UI 相关的代码编写过程。
设置 Data Binding
要使用 Data Binding,需要进行以下设置:
-
在 app 模块的 build.gradle 文件中启用 Data Binding:
android {
...
dataBinding {
enabled = true
}
}
2. 在布局文件顶部添加 <layout>
标签来包裹布局内容:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 布局内容 -->
</layout>
3.在 Activity 或 Fragment 中获取绑定的实例:
// 在 Activity 中通过 setContentView 方法绑定布局
MyLayoutBinding binding = DataBindingUtil.setContentView(this, R.layout.my_layout);
// 在 Fragment 中通过 LayoutInflater 绑定布局
MyLayoutBinding binding = MyLayoutBinding.inflate(inflater, container, false);
数据绑定表达式
可以在布局文件中使用表达式语言来引用绑定的数据和执行操作,例如:
-
引用变量或对象属性:
<TextView
android:text="@{user.name}"
... />
- 执行方法调用:
<Button
android:onClick="@{onClickListener.onButtonClick()}"
... />
- 使用条件语句和循环:
<TextView
android:text="@{user.isAdult ? `Adult` : `Child`}"
... />
<LinearLayout
android:visibility="@{user.isAdmin ? View.VISIBLE : View.GONE}"
... />
<RecyclerView
app:itemList="@{list}"
... />
双向绑定
双向绑定是一种数据绑定的概念,它使得视图(UI)和数据模型(Model)之间能够自动保持同步。当一个变量在视图中改变时,相关联的数据模型也会更新;反之,当数据模型的值发生变化时,视图也会相应地更新。
DataBinding 还支持双向绑定,即将 UI 组件的变化反映到数据源中。可以通过 @=
符号实现双向绑定:
<EditText
android:text="@={user.name}"
... />
二、例子
例1:DataBinding实现文本绑定和点击事件
MainActivity :
package com.example.databinding2;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.example.databinding2.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
// 利用DataBinding绑定UI文本
Food food = new Food("麻辣烫", (float) 4);
// 利用DataBinding实现点击事件
mainBinding.setOnClickListener(new OnClickListener(this));
mainBinding.setFood(food);
}
}
CountStart :
package com.example.databinding2;
public class CountStart {
public static String getStar(float star){
return star +"星";
}
}
Food:
package com.example.databinding;
public class Food {
public String name;
public float star;
public Food(String name,float star){
this.name = name;
this.star = star;
}
}
OnClickListener :
package com.example.databinding2;
import android.content.Context;
import android.view.View;
import android.widget.Toast;
public class OnClickListener {
private Context mContext;
public OnClickListener(Context context){
mContext = context;
}
public void buttonOnClick(View view){
Toast.makeText(mContext, "点击了提交!", Toast.LENGTH_SHORT).show();
}
}
activity_main:
<?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.databinding2.CountStart"/>
<variable
name="Food"
type="com.example.databinding2.Food" />
<!-- 实现点击事件 -->
<variable
name="OnClickListener"
type="com.example.databinding2.OnClickListener" />
</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/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="365dp" />
<ImageView
android:src="@drawable/img"
android:id="@+id/imageView"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.497"
tools:srcCompat="@tools:sample/avatars" />
<RatingBar
android:id="@+id/ratingBar"
android:layout_width="244dp"
android:layout_height="54dp"
android:max="5"
android:rating="@{Food.star}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2"
app:layout_constraintVertical_bias="0.079" />
<TextView
android:id="@+id/textView"
android:layout_width="129dp"
android:layout_height="28dp"
android:text="@{Food.name}"
android:textSize="20sp"
app:layout_constraintBottom_toTopOf="@+id/ratingBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.673"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.917" />
<Button
android:onClick="@{OnClickListener.buttonOnClick}"
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="160dp"
android:text="提交"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{CountStart.getStar(Food.star)}"
android:textSize="25sp"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ratingBar" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
build.gradle:
android {
compileSdk 32
defaultConfig {
applicationId "com.example.databinding"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
dataBinding {
enabled = true
}
}
运行结果:
例二:二级界面的绑定
activity_main:
<?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.databinding2.CountStart"/>
<variable
name="Food"
type="com.example.databinding2.Food" />
<!-- 实现点击事件 -->
<variable
name="OnClickListener"
type="com.example.databinding2.OnClickListener" />
</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/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="365dp" />
<ImageView
android:src="@drawable/img"
android:id="@+id/imageView"
android:layout_width="300dp"
android:layout_height="300dp"
app:layout_constraintBottom_toTopOf="@+id/guideline2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.497"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/textView"
android:layout_width="129dp"
android:layout_height="28dp"
android:text="@{Food.name}"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.673"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
app:Food="@{Food}"
app:OnClickListener="@{OnClickListener}"
layout="@layout/sub_main"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline2"
app:layout_constraintVertical_bias="0.253" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
sub_main:
<?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="Food"
type="com.example.databinding2.Food" />
<!-- 实现点击事件 -->
<variable
name="OnClickListener"
type="com.example.databinding2.OnClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RatingBar
android:id="@+id/ratingBar2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:rating="@{Food.star}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.438"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.065" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="32dp"
android:text="@{Food.name}"
android:textSize="25sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.449"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ratingBar2" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="提交"
android:onClick="@{OnClickListener.buttonOnClick}"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.463"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
其他代码和运行效果跟例一一样。
例三、双向绑定例子
MainActivity:
public class MainActivity extends AppCompatActivity {
private ViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 使用DataBindingUtil进行绑定
ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
// 初始化ViewModel
viewModel = new ViewModel();
// 设置ViewModel到绑定对象中
binding.setViewModel(viewModel);
}
}
ViewModel :
public class ViewModel extends BaseObservable {
private String inputText;
@Bindable
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
notifyPropertyChanged(BR.inputText);
}
}
activity_main:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.example.ViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@={viewModel.inputText}" />
<!-- 其他视图 -->
</LinearLayout>
</layout>
例四、使用@bindingAdapter注解加载图片
@BindingAdapter
注解是用于在 DataBinding 中绑定自定义属性和方法的注解。通过 @BindingAdapter
注解,我们可以定义一个静态方法,在该方法中实现各种自定义的数据绑定逻辑。
MainActivity :
package com.example.databinding32;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.os.Bundle;
import com.example.databinding32.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding mainBinding =DataBindingUtil.setContentView(this,R.layout.activity_main);
mainBinding.setNetworkImage("https://img-home.csdnimg.cn/images/20201124032511.png");
// mainBinding.setLocalImage(R.drawable.ic_launcher_background);
}
}
ImageBindingAdapter :
package com.example.databinding32;
import android.text.TextUtils;
import android.widget.ImageView;
import androidx.databinding.BindingAdapter;
import com.squareup.picasso.Picasso;
public class ImageBindingAdapter {
// 加载网络图片
@BindingAdapter("image")
public static void setImage(ImageView image,String url){
if (!TextUtils.isEmpty(url)){
Picasso.get()
.load(url)
.placeholder(R.drawable.ic_launcher_background)
.into(image);
}else {
image.setImageResource(R.drawable.ic_launcher_background);
}
}
// 加载本地图片
@BindingAdapter("image")
public static void setImage(ImageView image,int resId){
image.setBackgroundResource(resId);
}
// 如果没有url就加载本地图片
@BindingAdapter(value = {"image","defaultImageResources"},requireAll = false)
public static void setImage(ImageView image,String url,int resId){
if (!TextUtils.isEmpty(url)){
Picasso.get()
.load(url)
.placeholder(R.drawable.ic_launcher_background)
.into(image);
}else {
image.setBackgroundResource(resId);
}
}
}
activity_main:
<?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="networkImage"
type="String" />
<variable
name="localImage"
type="int" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="250dp"
android:layout_height="250dp"
app:image="@{networkImage}"
app:defaultImageResources="@{localImage}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
注意事项:
使用 @BindingAdapter
注解时,需要注意以下几点:
-
注解位置:
@BindingAdapter
注解可以应用于任何静态方法上。 -
方法参数:
@BindingAdapter
方法通常具有两个参数:视图对象(例如TextView
、ImageView
)和要绑定的属性值(例如字符串、图像资源等)。方法可以有任意多个参数,但前两个参数必须是视图对象和对应的属性值。 -
方法命名:为了与属性关联,方法名称通常以
"set"
开头,后面跟着要绑定的属性的名称。例如,如果要绑定imageUrl
属性,方法可以被命名为setImageUrl(ImageView view, String url)
。 -
参数注解:当
@BindingAdapter
方法有多个参数时,可以使用其他注解来标识不同的参数。例如,可以使用@BindingAdapter({"imageUrl", "placeholder"})
来指定方法的第一个参数对应的是imageUrl
属性,第二个参数对应的是placeholder
属性。 -
属性名称:在布局文件中,可以使用
app:属性名称
的方式来设置自定义的绑定属性。例如,app:imageUrl="@{viewModel.imageUrl}"
。
三、总结
使用 DataBinding 的主要意义在于简化安卓应用中的数据绑定和 UI 更新过程,提高开发效率和代码可读性。以下是使用 DataBinding 的一些重要意义:
1. 简化代码结构:通过 DataBinding,可以将布局文件和数据源直接绑定,从而减少了编写繁琐的 findViewById() 和手动设置数据的代码。这样可以使代码更加简洁、清晰,并且减少了因为手动更新 UI 导致的错误。
2. 减少空指针异常:DataBinding 使用空安全的表达式语言,可以避免由于数据为空而导致的空指针异常。通过在表达式中处理 null 值,可以确保安全地访问和操作数据。
3. 提高性能:Data Binding 可以实现数据与界面的实时绑定,只有当数据发生变化时才会触发 UI 的更新,相比传统的手动更新方式,能够有效地降低不必要的 UI 刷新,提高应用的性能和响应速度。
4. 支持双向绑定:DataBinding 支持双向绑定,即可以将用户对 UI 的修改反映到数据源中。这样可以轻松地实现表单输入、状态切换等功能,无需额外的回调或监听器。
5. 增强代码可读性:使用 DataBinding,可以将 UI 相关的代码和业务逻辑分离,使代码结构更加清晰,提高了代码的可读性和维护性。开发者可以专注于处理数据和逻辑层面,而不需要直接操作界面元素。
总之,使用 DataBinding 可以简化数据绑定过程、提高性能和代码可读性,减少错误,并提供了更灵活的数据绑定和双向绑定功能。它是一种强大的工具,可以提升安卓应用的开发效率和用户体验。
未完待续。。。。。。