基础篇
项目代码:放在前面以防有人看不到 https://github.com/summerhotready/KotlinCol/tree/bindingOrigin
基础篇讲述的是如何配置和使用db,并提供了稳定版本的参数,下面我们聊一聊使用最普遍的recyclerView是如何使用DB的
我们使用RV一般有两种情况,单布局和多布局,单布局简单明晰,就易用性来说比不上ListView,但倘若要做上下移动动画RV可就实在方便多了,RV的动画将放在另一篇写动画的帖子中总结
按照我们之前的书写方式,一个RecyclerView的展示包括:一个对应的Adapter,Modle,ViewHolder和adapter_item.xml。
组成List
你需要做的是:
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的值,当你一个页面有五六种布局的时候会比较麻烦,所以还是重写这个方法最好