本文将进行阐述如何通过DataBinding展示RecyclerView列表
先看一下效果:
1.每个item展示Student name 和Student age两个Button
2.点击name时,触发点击事件
3.点击age时,触发点击事件,同时修改该控件的paddingLeft值
一、Activity代码
1.activity:先大概看一遍,不需要都看懂,我们后面会讲
public class RecyclerBindActivity extends AppCompatActivity {
private ActivityRecyclerBindBinding binding;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bind();
}
private void bind() {
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_bind);
ArrayList<Student> students = getStudents();
SimpleBindAdapter adapter = new SimpleBindAdapter(students, R.layout.item_recycler_view);
adapter.setItemPresenter(new RecyclerBindPresenter());
binding.recyclerView
.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
binding.recyclerView
.setAdapter(adapter);
}
//presenter控制点击事件
public class RecyclerBindPresenter implements IBaseBindingPresenter {
public void onNameClick(Student student) {
Toast.makeText(RecyclerBindActivity.this, student.name.get() + "要改名字", Toast.LENGTH_SHORT).show();
student.name.set("我改名字啦!");
}
/**
* 点击用户年龄 += 3,且空间的左内边距+=3;
* {@link com.mei_husky.samplemvvm.view.bind.BindingUtil}
*/
public void onAgeClick(Student student) {
Toast.makeText(RecyclerBindActivity.this, String.valueOf("涨了三岁"), Toast.LENGTH_SHORT).show();
student.setAge(student.getAge()+3);
}
}
private ArrayList<Student> getStudents() {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("jack", 12));
students.add(new Student("rose", 13));
students.add(new Student("qingmei2", 18));
students.add(new Student("unknown", 21));
return students;
}
}
2.layout代码:
<?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>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</layout>
二、奇怪的Adapter
我们来看看Adapter代码:
public class SimpleBindAdapter extends BaseBindingAdapter<Student, ItemRecyclerViewBinding> {
public SimpleBindAdapter(List<Student> mDatas, int layoutId) {
super(mDatas, layoutId);
}
/**
* 如果有特殊需求可以实现在该方法中
* @param holder
*/
@Override
public void onCreateViewHolder(BaseViewHolder<ItemRecyclerViewBinding> holder) {
}
}
是不是很难理解!
Adapter怎么长成这样了?ViewHolder呢?
事实上这就是DataBinding Magic的地方,我们首先要知道ViewHolder的意义:用来处理数据和控件的交互,也就是说把数据放在控件上面,控件被点击之后我们再处理控件的点击事件,即:
数据 -> 控件
控件 -> 数据
而DataBinding能够实现的就是数据和控件的数据绑定,所以我们只要略微做出处理,就能够通过了了几行代码实现列表!实际上,大部分的列表需求都不需要额外的Adapter和ViewHolder!
我们如何才能基本抛弃Adapter和ViewHolder实现列表呢,或者说我们需要什么才能展示列表:
1、数据源Datas
我们当然需要数据源,这是我们需要去展示的,展示在item_layout上去
2、item的layoutId
我们知道了layoutId,就能通过DataBinding进行数据绑定。
3、UI调度器(Adapter和ViewHolder)
显然我们并不是太需要他们了,待会我们会实现一个BaseAdapter和BaseViewHolder,从而一次创建,之后到处使用就可以啦
4、点击事件处理器Presenter
细心的读者能发现,我们已经放在了Activity中的Presenter去处理所有的点击事件了,我们将点击事件的所有处理全部放在Presenter中,然后调用adapter.setItemPresenter()将响应事件传入即可。
三、强撸一波代码
0.item_layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="itemPresenter"
type="com.mei_husky.samplemvvm.view.activity.RecyclerBindActivity.RecyclerBindPresenter" />
<variable
name="data"
type="com.mei_husky.samplemvvm.model.Student" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:background="#ff0"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:layout_margin="16dp"
android:gravity="center_vertical"
android:onClick="@{() -> itemPresenter.onNameClick(data)}"
android:text="@{`Student Name : `+ data.name.get()}" />
<!--显示年龄,点击年龄+3,同时paddingleft也绑定年龄-->
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:textAllCaps="false"
android:gravity="center_vertical"
android:onClick="@{() -> itemPresenter.onAgeClick(data)}"
android:paddingLeft="@{data.age}"
android:text="@{`Student Age : `+data.age}" /> </LinearLayout>
</layout>
1.BaseBindingAdapter
public abstract class BaseBindingAdapter<T, D extends ViewDataBinding> extends RecyclerView.Adapter<BaseViewHolder<D>> {
private List<T> mDatas;
private int layoutId;
//用于设置Item的事件Presenter
protected IBaseBindingPresenter ItemPresenter;
public BaseBindingAdapter(List<T> mDatas, int layoutId) {
this.mDatas = mDatas;
this.layoutId = layoutId;
}
@Override
public BaseViewHolder<D> onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
BaseViewHolder<D> viewHolder = new BaseViewHolder<D>((D) DataBindingUtil.inflate(inflater, layoutId, parent, false));
onCreateViewHolder(viewHolder);
return viewHolder;
}
public abstract void onCreateViewHolder(BaseViewHolder<D> holder);
@Override
public void onBindViewHolder(BaseViewHolder<D> holder, int position) {
Log.i("tag", "onBindViewHolder");
holder.getBinding().setVariable(BR.data, mDatas.get(position));
holder.getBinding().setVariable(BR.itemPresenter, ItemPresenter);
holder.getBinding().executePendingBindings();
}
@Override
public int getItemCount() {
return mDatas == null ? 0 : mDatas.size();
}
/**
* 用于设置Item的事件Presenter
*
* @param itemPresenter
* @return
*/
public BaseBindingAdapter setItemPresenter(IBaseBindingPresenter itemPresenter) {
ItemPresenter = itemPresenter;
return this;
}
}
2、点击事件处理接口:
public interface IBaseBindingPresenter {}
3、BaseViewHolder:
public class BaseViewHolder<T extends ViewDataBinding> extends RecyclerView.ViewHolder {
protected final T binding;
public BaseViewHolder(T t) {
super(t.getRoot());
this.binding = t;
}
public T getBinding() {
return binding;
}}
4.在Activity中展示列表:
public class RecyclerBindActivity extends AppCompatActivity {
private ActivityRecyclerBindBinding binding;
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
bind();
}
private void bind() {
binding = DataBindingUtil.setContentView(this, R.layout.activity_recycler_bind);
ArrayList<Student> students = getStudents();
SimpleBindAdapter adapter = new SimpleBindAdapter(students, R.layout.item_recycler_view);
adapter.setItemPresenter(new RecyclerBindPresenter());
binding.recyclerView
.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
binding.recyclerView
.setAdapter(adapter);
}
//presenter控制点击事件
public class RecyclerBindPresenter implements IBaseBindingPresenter {
public void onNameClick(Student student) {
Toast.makeText(RecyclerBindActivity.this, student.name.get() + "要改名字", Toast.LENGTH_SHORT).show();
student.name.set("我改名字啦!");
}
/**
* 点击用户年龄 += 3,且空间的左内边距+=3;
* {@link com.mei_husky.samplemvvm.view.bind.BindingUtil}
*/
public void onAgeClick(Student student) {
Toast.makeText(RecyclerBindActivity.this, String.valueOf("涨了三岁"), Toast.LENGTH_SHORT).show();
student.setAge(student.getAge()+3);
}
}
private ArrayList<Student> getStudents() {
ArrayList<Student> students = new ArrayList<>();
students.add(new Student("jack", 12));
students.add(new Student("rose", 13));
students.add(new Student("qingmei2", 18));
students.add(new Student("unknown", 21));
return students;
}
}
5.创建BindUtil,处理点击age,paddingLeft+3的逻辑:
自定义 Setters
一些属性需要自定义绑定逻辑,android:paddingLeft 属性并没有对应的setter方法,但是存在setPadding(left, top, right, bottom)方法。通过 BindingAdapter 注释来自定义属性调用的静态setter方法。android 系统已经创建了 BindingAdapter 函数,下面是 paddingLeft 属性对应的函数:
public class BindingUtil {
/**
* 该效果展示在->MainActivity ->Databinding展示列表 -> 点击item的Age按钮
* {@link com.mei_husky.samplemvvm.view.activity.RecyclerBindActivity}
*
* @param view
* @param padding
*/
@BindingAdapter("android:paddingLeft")
public static void setPaddingLeft(View view, int padding) {
view.setPadding(padding,
view.getPaddingTop(),
view.getPaddingRight(),
view.getPaddingBottom());
}
}
该注解的详情,请看文末谷歌官方文档:
https://developer.android.com/topic/libraries/data-binding/index.html
到此,列表展示,需求实现。
总结
总结一下实现方式:
1.我们创建BaseViewHolder和BaseAdapter(之后这步就可以直接跳过)
2.绘制xml布局文件,variable导入datas数据和presenter响应事件
3.实现IBaseBindingPresenter接口自定义响应事件
4.activity中代码使用
其实看着可能有点头晕,但是敲一遍就能明白了,其实代码中还有一点需要优化的,就是并没有直接的方式在xml中给recyclerView设置Adapter和LayoutManager:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
app:adapter = "@{presenter.adapter}" //like this
android:layout_width="match_parent"
android:layout_height="match_parent" />
但其实依赖上文中的@BindingAdapter注解,实现起来也是非常简单,不赘述。