項目代碼:放在前面以防有人看不到 https://github.com/summerhotready/KotlinCol/tree/bindingOrigin
基礎篇講述的是如何配置和使用db,並提供了穩定版本的參數,下面我們聊一聊使用最普遍的recyclerView是如何使用DB的
我們使用RV一般有兩種情況,單佈局和多佈局,單佈局簡單明晰,就易用性來說比不上ListView,但倘若要做上下移動動畫RV可就實在方便多了,RV的動畫將放在另一篇寫動畫的帖子中總結
單佈局
按照我們之前的書寫方式,一個RecyclerView的展示包括:一個對應的Adapter,Modle,ViewHolder和adapter_item.xml。
組成List<Modle>數組後裝配Adapter,Adapter使用綁定好了的ViewHolder來建立item。
你需要做的是:
1.寫好item.xml佈局,並給各子view賦予modle的值
2.在ViewHolder中引用該Item的Binding並返回mBinding.root以替代ItemsView
3.在adapter的binding中賦值給holder.mBinding中綁定的modle
4.我想說的重點是監聽
網上最多的解釋是建立一個Holder類,通過在xml引用調用,我呢不想這樣,我寫了個接口,然後在xml中引用了,果然也成了。
public interface OnAdapterItemClickListener { //type int TYPE_EMAIL = 1; int TYPE_PHONE = 2; void onClickSave(int type,String... value); void onClickImportant(int type,String... value); void onClickSend(int type,String... values); }
xml引用
引入
<variable name="listener" type="com.guoxd.kot.listener.OnAdapterItemClickListener"/>使用
android:onClick="@{(view)->listener.onClickSend(listener.TYPE_EMAIL,data.email,data.name)}"
實現
要注意的是我還沒找到kotlin中匿名類的寫法,所以我寫了一個類實現了它,順便測試了一下String... 的kotlin實現
class Listener : OnAdapterItemClickListener{ override fun onClickImportant(type: Int, vararg value: String?) { for( str in value){ Log.i("Listener","onClickImportant Click:"+type+" string:"+str) } } override fun onClickSave(type: Int, vararg value: String?) { for( str in value){ Log.i("Listener","onClickSave Click:"+type+" string:"+str) } } override fun onClickSend(type: Int, vararg values: String?) { for( str in values){ Log.i("Listener","onClickSend Click:"+type+" string:"+str) } } }
賦值
adapter.listener = Listener()
Adapter中:聲明:var listener: OnAdapterItemClickListener?=null;
傳遞:onBindViewHolder中調用 holder?.bindListener(listener)
賦值:ViewHolder中的bindListener函數
fun bindListener(listener : OnAdapterItemClickListener?){ mBinding.setVariable(BR.listener,listener) }
多佈局
我參考了簡書上一篇博客,一開始我以爲它是服用Adapter和ViewHolder,來實現多個RecyclerView共用一套Adapter的問題,後來我發現不是,他解決的是一個RecyclerView中使用多種佈局的問題。
我在他文章的基礎上做了改變,對於數據源Bean,提供了抽象類BaseBean,並聲明一個抽象方法:getViewType()用來提供該數據源使用的layout,在我的設計中使用抽象類比原文的聲明接口更好理解和維護。關於接口和抽象類的定論在此不議,僅僅因爲無需再繼承其他的類所以此處用抽象類。
public abstract class BaseBean {
public abstract int getViewType();//要注意使用public聲明
}
在單佈局的基礎上我們可以製作多佈局,首先最重要的一點,將再ViewHolder中綁定layout的步驟挪到Adapter中,通過傳入ViewDataBinding的子類來實現,並且及其重要的一點,在ViewHolder中需要對滿足某一條件的modle賦值:
open class ContactBean(var name : String,var number : String,var Address : String,var isImportant : Boolean) : BaseBean(){
override fun getViewType(): Int {//回傳
return R.layout.adapter_contact_item
}
init {
isImportant = false
}
}
class ViewHolder(val mBinding : ViewDataBinding ) : RecyclerView.ViewHolder(mBinding.root){//替代的是ItemsView fun getBinding() : ViewDataBinding {//傳入ViewDataBinding的子類 return mBinding } fun bindData(bean:BaseBean){//賦值,BR是DB自動生成的,並看不到,可能會有多個可以相互替代不過選擇根目錄下的BR就可以了 mBinding.setVariable(BR.data,bean)//這一步是賦值,將BaseBean的實現/子類與data進行綁定,需要注意的是所有的adapter_item.xml的填充數據都要命名爲data } }
需要注意的是BR中的內容需要重新編譯纔會發生改變
綁定,Adapter中首先要重寫getItemViewType,以確保onCreateViewHolder中viewType的值非0.
override fun getItemViewType(position: Int): Int { return mDatas[position].getViewType() } // viewType:重寫getItemViewType後纔不是默認的0 override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder { val binding : ViewDataBinding = DataBindingUtil.inflate( LayoutInflater.from(mContext), viewType,parent,false ) return ViewHolder(binding) }
只有重寫了該方法create中獲得的viewType的值纔不會總是 0
這樣一來原本一一對應的adapter和viewholder被抽象出來,binding這一步被放到data中,viewholder反回一整個binding數據,而後在adapter中使用接口將複雜操作在引用中實現。
之後我測試了使用parent?.childCount獲取,但這樣有個問題是隻能呈現出頁面展示的item序號,比如頁面能展示4條,他只會生成5個於是我有個想法,在一個item內寫兩種佈局,引入兩個data,通過判空方式顯示data的值,當你一個頁面有五六種佈局的時候會比較麻煩,所以還是重寫這個方法最好
Adapter複用
下面談一談我真正想說的Adapter的複用,我是想實現一套adapter和viewHolder在多個RecyclerView上的使用,通過上一步驟,解耦了ViewHolder和molder,所以下一步是通過鉤子,將Adapter的onBindViewHolder傳遞到activity中,通過傳遞父類的數據類型,在處理頁面進行對應實現。參考 MultAdapter,這部分代碼爲什麼不放出?因爲跟詳細講解的多佈局是一樣的,在那基礎上引入了一個listener,要是看不懂去看看代碼吧。
總結一下,複用的越多要寫得越少,留給實現要做得越多。你可能覺得這是廢話,最初我看到的時候也覺得是廢話,等到寫了許多代碼才覺得這是實踐出真知。