前言:
RecyclerView在项目中的使用之频繁不用我再去过多的强调了。每一次我们使用RecyclerView的时候总要为RecyclerView写适配器,写 ViewHolder,并为Adapter转换数据等。如果我们需要为RecyclerView再添加Header,Footer,加载更多监听等,我们又不免要再写近百行代码。
可不可以不做这些?当然可以!下面请看MRecyclerView
MRecyclerView功能
已实现
1、动态的添加Header,Footer;
2、内置通用Adapter,不需要我们再写Adapter;
3、内置通用ViewHolder,不需要再写ViewHolder;
4、加载更多监听(支持GridLayoutManager和LinearLayoutManager,支持横向和纵向)
5、Header,Item,Footer点击事件监听
将要添加的功能
1、提供默认的Item进入动画
2、丰富加载更多动画
MRecyclerView解析
内置通用Adapter和ViewHolder实现
MViewHolder
//ViewHolder作用:复用已经加载过的item,减少绑定布局时间
/**
* 通用ViewHolder功能:将不同布局里面的view数量以及类型泛化,打造一个更为通用的ViewHolder
*/
inner class MViewHolder(val itemsView: View, val listener: MOnClickListener?) : RecyclerView.ViewHolder(itemsView) {
private var views = SparseArray<View>()
//通过在layout布局里的Id获取控件
init {
if (listener != null) {
itemsView.setOnClickListener {
listener.onClick(itemsView)
//如果listener是ItemListener,由于我们对于ItemListener通常要多使用一项position,所以在此设定
if (listener is OnItemClickedListener) {
//如果有header,我们需要将位置减去一之后再返回
if (hasHeader) {
listener.onClick(adapterPosition - 1)
} else {
listener.onClick(adapterPosition)
}
}
}
}
}
fun getView(viewId: Int): View? {
var view = views.get(viewId)
if (view == null) {
view = itemsView.findViewById(viewId)
views.put(viewId, view)
}
return view
}
}
MRecyclerViewAdapter
//数据显示适配器
private inner class MRecyclerViewAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
/**根据不同的ViewType,为MViewHolder传入不同的layout,从而构造不同类型的holder
*现在没有对异常进行捕获
*
* 对于点击事件监听的设置:Header,Footer的点击事件监听在onCreateViewHolder里面设置
* 但是NormalType的点击事件在onBindViewHolder里面设置,这样安排的主要原因是我们点击Item时,想要得到的回执数据主要是position
*/
override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
when (viewType) {
HEADER -> {
return buildHolder(parent, headerRes, headerClickedListener)
}
FOOTER -> {
if (footerRes != NO_LAYOUT_VALUE)
return buildHolder(parent, footerRes, footerClickedListener)
else //如果存在Footer类型,但却没有Footer布局文件的话,那么我们使用这个holder的构造器
return buildHolder(getDefaultLoadMoreView(),footerClickedListener)
}
}
return buildHolder(parent, itemRes, itemClickedListener)
}
override fun getItemCount(): Int {
var ret = dataSize
if (hasHeader) ret++
//如果dataSize为0,且设置了加载更多监听,那么不显示footer
if (dataSize != 0 || loadMoreListener != null){
if (hasFooter) ret++
}
return ret
}
//数据绑定,按照resId为View设定数值,我们在此要向外提供设定数值方法,因为像ImageView的图片加载,
// 我们往往是通过Glide等第三方图片加载框架
override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
//如果没有提供bindServer,那么抛出异常
if (bindDataServer == null) {
throw NoBindDataServerException("You should provide a server binding data to viewHolder!")
} else {
holder as MViewHolder
/**
* 当存在header的情况下,position包含了header,所以再根据position去取值得时候就会发生数组越界的问题
* 这个时候要将position的值减去1(对于header的position,它的值为-1,这一点我们需要注意)
*/
var positionModify = position
if (hasHeader) positionModify = position - 1
//这个应该需要修改,因为我们已经把数据提交到MRecyclerView内部了,这样写的话我们就使用不到我们提供的数据了。
bindDataServer?.OnBindData(holder, positionModify, getItemViewType(position))
}
}
//因为可能有Footer和Header,所以需要重写这个函数
override fun getItemViewType(position: Int): Int {
if (position == 0 && hasHeader) return HEADER