閱讀本篇文章須要讀者對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/