阅读本篇文章须要读者对Android Databinding和RecyclerView有必定的了解。android
咱们知道,DataBinding
的核心理念是数据驱动。数据驱动驱动的目标就是View
,使用DataBinding
,咱们经过添加、修改、删除数据源,View
就会自动予以相关变化。git
Android RecyclerView的Adapter起的做用就是链接数据和View。github
一个最简单的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()
,让每一个子项得以显示正确的数据。ide
能够看到,Adapter
起的做用和DataBinding
是很是相似的,使用DataBinding
,能够使Adapter
的编写显得更加简单。函数
接下来看一个简单的例子。这个例子建立了一个简单的列表,效果以下:布局
咱们看看,使用DataBinding
该如何实现它。优化
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; } }
<?xml version="1.0" encoding="utf-8"?> <layout> <data> <import type="cn.zmy.databindingadapter.model.User"/> <variable name="model" type="User"/> </data> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="60dp" android:layout_marginBottom="10dp" android:background="@android:color/darker_gray" android:gravity="center_vertical" android:orientation="vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{model.name}"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{String.valueOf(model.age)}"/> </LinearLayout> </layout>
public class UserAdapter extends RecyclerView.Adapter { private Context context; private List<User> items; public UserAdapter(Context context) { this.context = context; this.items = new ArrayList<User>() {{ 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); } } }
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
还能不能更简单呢?
咱们发现,Adapter
中的UserViewHolder
几乎没有作任何事。事实上,咱们声明它彻底是因为Adapter
的onCreateViewHolder
须要这么一个返回值。
咱们能够把ViewHolder提出来,这样全部Adapter
均可以使用而无需在每一个Adapter
中都声明一个ViewHolder。
取名就叫BaseBindingViewHolder
public class BaseBindingViewHolder extends RecyclerView.ViewHolder { public BaseBindingViewHolder(View itemView) { super(itemView); } }
getItemCount
返回了子项的数量。
因为几乎每一个Adapter
都会存在一个List用于保存全部子项的数据,咱们彻底能够建立一个Adapter基类,而后在基类中实现getItemCount
。
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<M, B extends ViewDataBinding> extends RecyclerView.Adapter { protected Context context; protected List<M> 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<User, ItemUserBinding> { 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
是Android DataBinding库中的一个类。
public class ObservableArrayList<T> extends ArrayList<T> implements ObservableList<T> { ... }
ObservableArrayList
实现了ObservableList
接口。经过ObservableList
,咱们能够为ObservableArrayList
添加一个或多个Listener。当ObservableArrayList
中的数据发生变化时(添加了一个或多个元素、删除了其中某个或某些元素时),这些Listener或收到数据源发生改变的通知。
其实ObservableArrayList
的实现并不复杂,只须要重写add
、addAll
、remove
等等等等这些可能形成集合发生变化的方法就能够实现上述效果。
虽然实现不复杂,可是ObservableArrayList
却能够解决咱们上面遇到的修改数据源的问题。
咱们只须要在集合发生改变时,调用adapter.notifyXXX()
等方法就能够实现当数据源发生变化时,View也能够自动发生变化,而外部却无需调用adapter.notifyXXX()
了。
咱们再次修改BaseBindingAdapter
的代码,使之支持数据源发生变化时,自动更新View。
public abstract class BaseBindingAdapter<M, B extends ViewDataBinding> extends RecyclerView.Adapter { protected Context context; protected ObservableArrayList<M> items; protected ListChangedCallback itemsChangeCallback; public BaseBindingAdapter(Context context) { this.context = context; this.items = new ObservableArrayList<>(); this.itemsChangeCallback = new ListChangedCallback(); } public ObservableArrayList<M> 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<M> newItems) { resetItems(newItems); notifyDataSetChanged(); } protected void onItemRangeChanged(ObservableArrayList<M> newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeChanged(positionStart,itemCount); } protected void onItemRangeInserted(ObservableArrayList<M> newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeInserted(positionStart,itemCount); } protected void onItemRangeMoved(ObservableArrayList<M> newItems) { resetItems(newItems); notifyDataSetChanged(); } protected void onItemRangeRemoved(ObservableArrayList<M> newItems, int positionStart, int itemCount) { resetItems(newItems); notifyItemRangeRemoved(positionStart,itemCount); } protected void resetItems(ObservableArrayList<M> newItems) { this.items = newItems; } //endregion protected abstract @LayoutRes int getLayoutResId(int viewType); protected abstract void onBindItem(B binding, M item); class ListChangedCallback extends ObservableArrayList.OnListChangedCallback<ObservableArrayList<M>> { @Override public void onChanged(ObservableArrayList<M> newItems) { BaseBindingAdapter.this.onChanged(newItems); } @Override public void onItemRangeChanged(ObservableArrayList<M> newItems, int i, int i1) { BaseBindingAdapter.this.onItemRangeChanged(newItems,i,i1); } @Override public void onItemRangeInserted(ObservableArrayList<M> newItems, int i, int i1) { BaseBindingAdapter.this.onItemRangeInserted(newItems,i,i1); } @Override public void onItemRangeMoved(ObservableArrayList<M> newItems, int i, int i1, int i2) { BaseBindingAdapter.this.onItemRangeMoved(newItems); } @Override public void onItemRangeRemoved(ObservableArrayList<M> 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)); } }
能够看到,外部仅仅将数据添加到了数据源中,而没有作任何其余操做。不过咱们的View仍是更新了,效果和上面是同样的。这也符合DataBinding的核心原则:数据驱动。使用DataBinding,咱们关心的只有数据源,只要数据源发生改变,View就应随之发生改变。
文章中的代码已整理上传至Github。 连接:https://github.com/a3349384/DataBindingAdapter 博客:https://www.zhoumingyao.cn/