Android RecyclerView结合DataBinding和LiveData

本文目的:记录使用DataBinding实现对拥有RecyclerView组件的页面进行较大程度的解耦的方式。

RecyclerView数据源:从本地SQLite数据库中使用Room获取一个LiveData并存储在ViewModel中:

public class StudentViewModel extends AndroidViewModel {

    private StudentRepository repository;
    private LiveData<List<Student>> studentsLD;

    public StudentViewModel(@NonNull Application application) {
        super(application);
        this.repository=new StudentRepository(application);
        studentsLD=repository.getAllStudentsLive();
    }

    public void insert(){
        Student s1=new Student("jack",18);
        Student s2=new Student("rose",17);
        insertStudents(s1,s2);
    }

    public void update(){
        Student s1=new Student(3,"jason",20);
        updateStudents(s1);
    }

    public void clear(){
        deleteAllStudents();
    }

    public void delete(){
        Student s1=new Student(2,"rose",17);
        deleteStudents(s1);
    }


    private void insertStudents(Student... students){
        repository.insertStudents(students);
    }

    private void updateStudents(Student... students){
        repository.updateStudents(students);
    }

    private void deleteStudents(Student... students){
        repository.deleteStudents(students);
    }

    private void deleteAllStudents(){
        repository.deleteAllStudents();
    }

//  错误!!!
//    public LiveData<List<Student>> getAllStudentLive(){
//        return repository.getAllStudentsLive();
//    }

//   正确
    public LiveData<List<Student>> getStudentsLD() {
        return studentsLD;
    }
}

将获取到的LiveData<List<Student>>对象通过DataBinding传给Activity的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"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="viewModel"
            type="com.shopkeeper.room2.StudentViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <Button
            android:id="@+id/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="60dp"
            android:text="增加"
            android:onClick="@{()->viewModel.insert()}"
            app:layout_constraintBottom_toBottomOf="@+id/button2"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="@+id/button2" />

        <Button
            android:id="@+id/button2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="60dp"
            android:layout_marginEnd="60dp"
            android:text="删除"
            android:onClick="@{()->viewModel.delete()}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <Button
            android:id="@+id/button3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="60dp"
            android:text="修改"
            android:onClick="@{()->viewModel.update()}"
            app:layout_constraintEnd_toEndOf="@+id/button"
            app:layout_constraintStart_toStartOf="@+id/button"
            app:layout_constraintTop_toBottomOf="@+id/button" />

        <Button
            android:id="@+id/button4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="清空"
            android:onClick="@{()->viewModel.clear()}"
            app:layout_constraintBottom_toBottomOf="@+id/button3"
            app:layout_constraintEnd_toEndOf="@+id/button2"
            app:layout_constraintStart_toStartOf="@+id/button2"
            app:layout_constraintTop_toTopOf="@+id/button3" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recycleView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="30dp"
            app:studentBinder="@{viewModel.studentsLD}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/button3" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

此处定义了一个studentBinder的BindingAdapter,用于处理传递过来的数据:

public class RVAdapter {

    @BindingAdapter("studentBinder")
    public static void studentsBindAdapter(RecyclerView recyclerView, List<Student> students){
        StudentAdapter adapter = (StudentAdapter) recyclerView.getAdapter();
        assert adapter != null;
        adapter.setStudents(students);
    }
}

由于绑定的是LiveData数据,所以每当列表发生改变时,就会自动执行BindingAdapter中的方法,进而通过RecyclerView的Adapter更新并显示数据(每一个Item也是通过DataBinding来绑定数据的)。Adapter内容:

public class StudentAdapter extends RecyclerView.Adapter<StudentAdapter.StudentViewHolder> {

    List<Student> students;

    public StudentAdapter(List<Student> students) {
        this.students = students;
    }

    @NonNull
    @Override
    public StudentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        ItemBinding binding= DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),R.layout.item,parent,false);
        return new StudentViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull StudentViewHolder holder, int position) {
        holder.binding.setStudent(students.get(position));
    }

    @Override
    public int getItemCount() {
        return students==null?0:students.size();
    }

    static class StudentViewHolder extends RecyclerView.ViewHolder{
        ItemBinding binding;
        public StudentViewHolder(@NonNull ItemBinding binding) {
            super(binding.getRoot());
            this.binding=binding;
        }
    }

    public void setStudents(List<Student> students) {
        this.students = students;
        notifyDataSetChanged();
    }
}

通过这种写法,在Activity中的代码量就很少了,甚至不需要使用Observer来监听LiveData的数据变化:

public class MainActivity extends AppCompatActivity {

    ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding= DataBindingUtil.setContentView(this,R.layout.activity_main);
        StudentViewModel viewModel = new ViewModelProvider(this,
                new ViewModelProvider.AndroidViewModelFactory(getApplication()))
                .get(StudentViewModel.class);
        binding.recycleView.setAdapter(new StudentAdapter(viewModel.getStudentsLD().getValue()));
        binding.recycleView.setLayoutManager(new LinearLayoutManager(this));
        binding.setViewModel(viewModel);
        binding.setLifecycleOwner(this);
    }
}

效果图:

发表评论