极简的Android RecyclerView Adapter(使用DataBinding)

简介 我们知道,DataBinding的核心理念是数据驱动。数据驱动驱动的目标就是View,使用DataBinding,我们通过添加、修改、删除数据源,View就会自动予以相关变化。
Android RecyclerView的Adapter起的作用就是连接数据和View。
一个最简单的RecyclerView Adapter可能是下面这个样子的:

public class UserAdapter extends RecyclerView.Adapter { @Override public int getItemCount() { return 0; }@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return null; }@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {} }

通过getItemsCount(), RecyclerView知道了所有子项的数量。
通过onCreateViewHolder(), RecyclerView知道了每一个子项长什么样子。
通过onBindViewHolder(),让每个子项得以显示正确的数据。
可以看到,Adapter起的作用和DataBinding是非常类似的,使用DataBinding,可以使Adapter的编写显得更加简单。
DataBinding简单使用 接下来看一个简单的例子。这个例子创建了一个简单的列表,效果如下:

我们看看,使用DataBinding该如何实现它。
Model类:
public class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; }public String getName() { return name; }public void setName(String name) { this.name = name; }public int getAge() { return age; }public void setAge(int age) { this.age = age; } }

View xml

Adapter
public class UserAdapter extends RecyclerView.Adapter { private Context context; private List items; public UserAdapter(Context context) { this.context = context; this.items = new ArrayList() {{ add(new User("张三", 18)); add(new User("李四", 28)); add(new User("王五", 38)); }}; }@Override public int getItemCount() { return this.items.size(); }@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemUserBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), R.layout.item_user, parent, false); return new UserViewHolder(binding.getRoot()); }@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ItemUserBinding binding = DataBindingUtil.getBinding(holder.itemView); binding.setModel(this.items.get(position)); binding.executePendingBindings(); }static class UserViewHolder extends RecyclerView.ViewHolder { public UserViewHolder(View itemView) { super(itemView); } } }

Activity
public class MainActivity extends AppCompatActivity {@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(new UserAdapter(this)); } }

可以看到,使用了DataBinding之后,我们在onBindViewHolder中,无需再写一些类似于holder.view.setXXX()的代码,因为这些在Xml中就已经完成了。
优化 上面的Adapter还能不能更简单呢?
优化ViewHolder 我们发现,Adapter中的UserViewHolder几乎没有做任何事。事实上,我们声明它完全是由于AdapteronCreateViewHolder需要这么一个返回值。
我们可以把ViewHolder提出来,这样所有Adapter都可以使用而无需在每个Adapter中都声明一个ViewHolder。
取名就叫BaseBindingViewHolder
public class BaseBindingViewHolder extends RecyclerView.ViewHolder { public BaseBindingViewHolder(View itemView) { super(itemView); } }

优化getItemCount getItemCount返回了子项的数量。
由于几乎每个Adapter都会存在一个List用于保存所有子项的数据,我们完全可以创建一个Adapter基类,然后在基类中实现getItemCount
优化onCreateViewHolder&onBindViewHolder onCreateViewHolder代码如下:
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ItemUserBinding binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), R.layout.item_user, parent, false); return new BaseBindingViewHolder(binding.getRoot()); }

可以看到,这个方法里面唯一的“变数”就是“R.layout.item_user”这个layout。
onBindViewHolder代码如下:
@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ItemUserBinding binding = DataBindingUtil.getBinding(holder.itemView); binding.setModel(this.items.get(position)); binding.executePendingBindings(); }

可以看到,这个方法先获取到View的Binding,然后给Binding的Data赋值。Binding从哪里来?都是通过DataBindingUtil.getBinding(holder.itemView)获取到的。
本着不写重复代码,能封装就封装的原则,我们来创建Adapter基类。代码如下:
public abstract class BaseBindingAdapter extends RecyclerView.Adapter { protected Context context; protected List items; public BaseBindingAdapter(Context context) { this.context = context; this.items = new ArrayList<>(); }@Override public int getItemCount() { return this.items.size(); }@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { B binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), this.getLayoutResId(viewType), parent, false); return new BaseBindingViewHolder(binding.getRoot()); }@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { B binding = DataBindingUtil.getBinding(holder.itemView); this.onBindItem(binding, this.items.get(position)); }protected abstract @LayoutRes int getLayoutResId(int viewType); protected abstract void onBindItem(B binding, M item); }

然后使UserAdapter继承自上面封装的BaseBindingAdapter,代码如下:
public class UserAdapter extends BaseBindingAdapter { public UserAdapter(Context context) { super(context); items.add(new User("张三", 18)); items.add(new User("李四", 28)); items.add(new User("王五", 38)); }@Override protected int getLayoutResId(int viewType) { return R.layout.item_user; }@Override protected void onBindItem(ItemUserBinding binding, User user) { binding.setModel(user); binding.executePendingBindings(); } }

可以看到,优化后的Adapter除去初始化User数据源的那部分代码,实际上的核心代码就寥寥数行。
通过getLayoutResId我们告诉了RecyclerView子项长什么样子。
通过onBindItem我们给具体的每个子项绑定了合适的数据。
至于具体的绑定过程,是放在布局的xml文件中的。
优化数据源 我们的数据源是在构造函数中这样添加的:
items.add(new User("张三", 18)); items.add(new User("李四", 28)); items.add(new User("王五", 38));

在实际开发过程中,我们极少这么做。因为通常在构造Adapter的时候,我们并未得到任何有效的数据。数据源可能是通过网络请求从服务器得来,也可能是通过查询本地数据库表得来。我们在构造Adapter之后,可能还需要较长的时间去获取有效的数据源,这就要求必须在Adapter构造完成之后,外部调用者还可以修改的数据源。
我们可以这样做:
adapter.items.add(XXX); adapter.notifyItemInserted();

这样我们新增数据源之后,adapter也知道我们修改了数据源,进而View也就能随之变化。
不过有了DataBinding,我们可以更为巧妙的实现上述操作。
ObservableArrayList
ObservableArrayList是Android DataBinding库中的一个类。
public class ObservableArrayList extends ArrayList implements ObservableList { ... }

ObservableArrayList实现了ObservableList接口。通过ObservableList,我们可以为ObservableArrayList添加一个或多个Listener。当ObservableArrayList中的数据发生变化时(添加了一个或多个元素、删除了其中某个或某些元素时),这些Listener或收到数据源发生改变的通知。
其实ObservableArrayList的实现并不复杂,只需要重写addaddAllremove等等等等这些可能造成集合发生变化的方法就可以实现上述效果。
虽然实现不复杂,但是ObservableArrayList却可以解决我们上面遇到的修改数据源的问题。
我们只需要在集合发生改变时,调用adapter.notifyXXX()等方法就可以实现当数据源发生变化时,View也可以自动发生变化,而外部却无需调用adapter.notifyXXX()了。
代码实现
我们再次修改BaseBindingAdapter的代码,使之支持数据源发生变化时,自动更新View。
public abstract class BaseBindingAdapter extends RecyclerView.Adapter { protected Context context; protected ObservableArrayList items; protected ListChangedCallback itemsChangeCallback; public BaseBindingAdapter(Context context) { this.context = context; this.items = new ObservableArrayList<>(); this.itemsChangeCallback = new ListChangedCallback(); }public ObservableArrayList getItems() { return items; }@Override public int getItemCount() { return this.items.size(); }@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { B binding = DataBindingUtil.inflate(LayoutInflater.from(this.context), this.getLayoutResId(viewType), parent, false); return new BaseBindingViewHolder(binding.getRoot()); }@Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { B binding = DataBindingUtil.getBinding(holder.itemView); this.onBindItem(binding, this.items.get(position)); }@Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); this.items.addOnListChangedCallback(itemsChangeCallback); }@Override public void onDetachedFromRecyclerView(RecyclerView recyclerView) { super.onDetachedFromRecyclerView(recyclerView); this.items.removeOnListChangedCallback(itemsChangeCallback); }//region 处理数据集变化 protected void onChanged(ObservableArrayList newItems) { resetItems(newItems); notifyDataSetChanged(); }protected void onItemRangeChanged(ObservableArrayList newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeChanged(positionStart,itemCount); }protected void onItemRangeInserted(ObservableArrayList newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeInserted(positionStart,itemCount); }protected void onItemRangeMoved(ObservableArrayList newItems) { resetItems(newItems); notifyDataSetChanged(); }protected void onItemRangeRemoved(ObservableArrayList newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeRemoved(positionStart,itemCount); }protected void resetItems(ObservableArrayList newItems) { this.items = newItems; } //endregionprotected abstract @LayoutRes int getLayoutResId(int viewType); protected abstract void onBindItem(B binding, M item); class ListChangedCallback extends ObservableArrayList.OnListChangedCallback> { @Override public void onChanged(ObservableArrayList newItems) { BaseBindingAdapter.this.onChanged(newItems); }@Override public void onItemRangeChanged(ObservableArrayList newItems, int i, int i1) { BaseBindingAdapter.this.onItemRangeChanged(newItems,i,i1); }@Override public void onItemRangeInserted(ObservableArrayList newItems, int i, int i1) { BaseBindingAdapter.this.onItemRangeInserted(newItems,i,i1); }@Override public void onItemRangeMoved(ObservableArrayList newItems, int i, int i1, int i2) { BaseBindingAdapter.this.onItemRangeMoved(newItems); }@Override public void onItemRangeRemoved(ObservableArrayList sender, int positionStart, int itemCount) { BaseBindingAdapter.this.onItemRangeRemoved(sender,positionStart,itemCount); } } }

然后我们修改Activity的代码:
public class MainActivity extends AppCompatActivity {@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView); recyclerView.setLayoutManager(new LinearLayoutManager(this)); UserAdapter adapter = new UserAdapter(this); recyclerView.setAdapter(adapter); adapter.getItems().add(new User("张三", 18)); adapter.getItems().add(new User("李四", 28)); adapter.getItems().add(new User("王五", 38)); } }

【极简的Android RecyclerView Adapter(使用DataBinding)】可以看到,外部仅仅将数据添加到了数据源中,而没有做任何其他操作。不过我们的View还是更新了,效果和上面是一样的。这也符合DataBinding的核心原则:数据驱动。使用DataBinding,我们关心的只有数据源,只要数据源发生改变,View就应随之发生改变。

    推荐阅读