DataBinding的使用

什么是 DataBinding

DataBinding 是一个让你可以把布局和数据绑定起来的库,我们可以通过 官方文档 来学习如何使用 DataBinding。

我们一般通过这种方式给 UI 设置数据:

java 复制代码
TextView textView = findViewById(R.id.sample_text);
if(textView != null && user != null){
    textView.setText(user.getName());
}

使用 DataBinding 后,上面这些代码都不需要写了,直接通过配置布局文件就可以实现上面代码的功能,注意下面的@{}语法。

xml 复制代码
<TextView
    android:text="@{user.name}" />

使用 DataBinding 的好处:

    1. findViewById、setText 这些代码都不用写了,代码更容易维护。
    1. textView 的空检查不需要了,因为绑定的逻辑写在 textView 的布局上,所以不存在 textView 为 null 的问题;user 的空检查也不需要了,如果 user 为 null,user.name 就会被分配默认值 null。
    1. 数据更新的时候不再需要重复调用 setText 了。

如何使用

使用 DataBinding 之前记得在 build.gradle 中启用 DataBinding:

arduino 复制代码
android {
    ...
    buildFeatures {
        dataBinding true
    }
}

在想要使用 DataBinding 的布局的根布局上右键,选择 Show Context Actions,然后选择 Convert to data binding layout。

这里我们实现一个显示学生信息列表的功能,此外还包含两个按钮,一个用于添加学生,一个用于删除学生。

activity_main.xml:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <variable
            name="clickPresenter"
            type="com.example.test.MainActivity.ClickPresenter" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <Button
            android:text="添加学生"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickPresenter::addStudent}"/>

        <Button
            android:text="删除学生"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="@{clickPresenter::removeStudent}"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_student_list"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

    </LinearLayout>
</layout>

在 activity_main.xml 中配置添加学生和删除学生按钮分别调用 ClickPresenter 中的 addStudent() 和 removeStudent() 方法。

MainActivity 中的代码如下:

java 复制代码
public class MainActivity extends AppCompatActivity {

    private StudentListAdapter mStudentListAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding mActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        mActivityMainBinding.rvStudentList.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL, false));
        mStudentListAdapter = new StudentListAdapter(getStudentList());
        mActivityMainBinding.rvStudentList.setAdapter(mStudentListAdapter);

        mActivityMainBinding.setClickPresenter(new ClickPresenter());
    }


    private List<Student> getStudentList() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("小明","001"));
        list.add(new Student("小红","002"));
        list.add(new Student("小刚","003"));
        list.add(new Student("小强","004"));
        return list;
    }

    public class ClickPresenter {

        public void addStudent(View view) {
            Toast.makeText(MainActivity.this, "addUser", Toast.LENGTH_SHORT).show();
            mStudentListAdapter.addData(new Student("小张","005"));
            mStudentListAdapter.notifyDataSetChanged();
        }

        public void removeStudent(View view) {
            Toast.makeText(MainActivity.this, "removeUser", Toast.LENGTH_SHORT).show();
            if(mStudentListAdapter.getItemCount() > 0){
                mStudentListAdapter.remove(0);
                mStudentListAdapter.notifyDataSetChanged();
            }
        }
    }
}

在 MainActivity 中使用 DataBindingUtil 的 setContentView() 方法绑定了上面的布局文件并对 ClickPresenter 中的方法进行了实现。

Student 类的代码如下:

java 复制代码
public class Student extends BaseObservable {
    private String name;
    private String id;
    public Student(String name, String id) {
        this.name = name;
        this.id = id;
    }

    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
        notifyPropertyChanged(BR.id);
    }
}

展示学生信息对应的布局文件为:item_student.xml

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="student"
            type="com.example.test.Student" />
    </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="@{student.id}"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{student.name}"/>
    </LinearLayout>
</layout>

RecyclerView 还需要结合 Adapter 来展示列表信息,代码如下:

java 复制代码
public class StudentListAdapter extends RecyclerView.Adapter<StudentListAdapter.StudentItemViewHolder> {

    private List<Student> mStudentList;

    public StudentListAdapter(List<Student> list) {
        mStudentList = list;
    }

    @NonNull
    @Override
    public StudentItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_student, null);
        return new StudentItemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull StudentItemViewHolder holder, int position) {
        holder.getItemStudentBinding().setStudent(mStudentList.get(position));
        holder.getItemStudentBinding().executePendingBindings();
    }

    @Override
    public int getItemCount() {
        return mStudentList.size();
    }

    public void addData(Student student) {
        mStudentList.add(student);
    }

    public void remove(int index) {
        mStudentList.remove(index);
    }

    public static class StudentItemViewHolder extends RecyclerView.ViewHolder {

        private ItemStudentBinding binding;

        public StudentItemViewHolder(View itemView) {
            super(itemView);
            binding = DataBindingUtil.bind(itemView);
        }

        public ItemStudentBinding getItemStudentBinding() {
            return binding;
        }
    }
}

在 StudentItemViewHolder 中使用 DataBindingUtil 的 bind() 方法来绑定列表的 item 布局,并返回 ItemStudentBinding 实例,这样 StudentItemViewHolder 持有 ItemStudentBinding 实例就行了,不需要持有里面的每一个 View 了,同时省略了 findViewById 的代码。在 StudentListAdapter 中也不需要分别给 item 中的控件设值了,直接调用 ItemStudentBinding 实例的 setStudent() 即可。

Binding Adapters

现在要显示学生头像,我想用 Glide,头像来源于图片链接,同时还需要提供占位图。由于没办法直接在 ImageView 布局中配置图片链接和占位图,使用 DataBinding 该如何实现?

这时候就可以使用 BindingAdapter,添加如下代码:

java 复制代码
public class CommonUtils {

    @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);
    }
}

然后就可以直接在 ImageView 的布局中配置 imageUrl 和 placeHolder 了:

xml 复制代码
<ImageView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:imageUrl="@{student.url}"
    app:placeHolder="@{@drawable/student_icon}"/>

这样只要配置了 imageUrl 和 placeHolder,注解 @BindingAdapter 对应的方法就会被调用。如果你希望配置了 imageUrl 和 placeHolder 中的某一个,就调用该方法,需要添加 requireAll = false,代码如下:

kotlin 复制代码
@BindingAdapter({"app:imageUrl", "app:placeHolder"}, requireAll = false)

结合 LiveData

前面 variable 中的 Student 类必须要继承 BaseObservable 或使用 ObservableField,还要添加注解 @Bindable、调用notifyPropertyChanged(BR.name),目的是为了在 student.setName(name) 时通知对应的 View 更新。

现在有了 LiveData,LiveData 使用数据驱动,可以不需要使用 BaseObservable 或 ObservableField 了,并且还可以自动管理生命周期,避免内存泄漏。代码如下:

xml 复制代码
// student.xml
<?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">
    <data>
        <variable
            name="viewmodel"
            type="com.example.test.ScheduleViewModel" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewmodel.studentname}"/>
    </LinearLayout>

</layout>
kt 复制代码
class ViewModelActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: StudentBinding = DataBindingUtil.setContentView(this, R.layout.student)
        binding.lifecycleOwner = this

        binding.viewmodel = ViewModelProvider(this)[ScheduleViewModel::class.java]
    }
}
kt 复制代码
class ScheduleViewModel : ViewModel() {

    private val _studentname = MutableStateFlow("")

    val studentname: StateFlow<String> = _studentname

    init {
        viewModelScope.launch {
            delay(5000)
            _studentname.value = "小王"
        }
    }
}

可以看到不需要调用 LiveData 的 observe(owner,observer) 了,DataBinding 帮我们完成了数据驱动。

相关推荐
Libraeking11 小时前
破壁行动:在旧项目中丝滑嵌入 Compose(混合开发实战)
android·经验分享·android jetpack
Libraeking2 天前
导航之弦:Compose Navigation 的深度解耦与类型安全
经验分享·android jetpack
撩得Android一次心动2 天前
Android LiveData 全面解析:使用Java构建响应式UI【源码篇】
android·java·android jetpack·livedata
符哥20084 天前
关于用Android Compose开发成不成熟的分析
android·android jetpack
蹦哒7 天前
Jetpack Compose Surface 完全指南
android jetpack
我命由我123457 天前
Android 开发 Room 数据库升级问题:A migration from 6 to 7 was required but not found.
android·java·java-ee·android studio·android jetpack·android-studio·android runtime
我命由我123459 天前
Android 控件 - 最简单的 Notification、Application Context 应用于 Notification
android·java·开发语言·junit·android studio·android jetpack·android-studio
工程师老罗9 天前
我用Ai学Android Jetpack Compose之Text
android·android jetpack
tangweiguo030519879 天前
Android Jetpack Compose 面试题大全(2025最新整理)
android·android jetpack
安卓开发者9 天前
Android Jetpack Compose:现代声明式UI开发指南
android·ui·android jetpack