自定义Adapter给RecyclerView添加头和尾
HelloAdapter能做什么?
1、动态的添加头布局和尾布局。
2、不限数量的添加头布局和尾布局。
3、添加头布局和尾布局时可以指定view在顶部和尾部中的位置(可根据view的下标添加,也可以根据view的下标移除)。
4、给添加的头布局和尾布局中的子view设置点击事件。
5、数据为空时,头和尾之间显示“再无数据”页面(可自定义“再无数据”页面)。
6、RecyclerView为多布局时,可在自定义Holder中做数据绑定,减少adapter中的代码复杂度。
7、设置item的子view的点击事件或长按事件。
怎么简单的给Adapter添加头和尾以及暂无数据的提示???
封装一个baseAdapter,在baseAdapter的第一和第二个item添加两个固定的布局,分别是放“头”的布局和“暂无数据”的布局,在baseAdapter的最后添加一个item放“尾”的布局item。
自定义HelloAdapter
addFootView 方法仅添加尾布局
addHeadView 方法仅添加头布局
实现OnHeadAndFootClick接口并通过如下设置监听
addFootAndClickListener 方法添加尾布局并对尾布局中的view设置点击监听,会接收一个view的id集合。
addHeadAndClickListener 方法添加头布局并对头布局中的view设置点击监听,会接收一个view的id集合。
实现OnHeadAndFootClick即可
TestAdapter继承自HelloAdapter,初始化Adapter。
adapter = TestAdapter(this)
.setLayoutId(R.layout.item_layout) // 设置item的布局文件(必选)
.setData(data) // 设置adapter的数据集合(必选)
.setEmptyLayoutId(R.layout.item_empty_layout_a) // 设置adapter无数据时的布局(可选,有默认布局)
.showEmptyLayout(true) // adapter无数据时是否显示“暂无数据”的布局(默认显示)
头布局设置点击事件的view id
var layoutIds: MutableList<Int> = arrayListOf()
layoutIds.add(R.id.head_bt1)
layoutIds.add(R.id.head_bt2)
layoutIds.add(R.id.head_bt3)
设置头布局,并设置头布局中子view的点击事件
val header: View = adapter?.addHeaderAndClickListener(R.layout.header_layout3,layoutIds)!!
只添加头布局,不设置点击监听
val header: View = adapter?.addHeaderView(R.layout.header_layout3)!!
设置尾布局,并设置头布局中子view的点击事件
val header: View = adapter?.addFooterAndClickListener(R.layout.header_layout3,layoutIds)!!
只添加尾布局,不设置点击监听
val header: View = adapter?.addFooterView(R.layout.header_layout3)!!
自定义adapter后继承HelloAdapter并实现抽象方法bindViewHolder,并在bindViewHolder中绑定数据。
如果是多布局adapter,可复写getItemViewHelloType方法和onCreateViewHelloHolder方法,在getItemViewHelloType方法中返回自定义的布局类型viewType,在onCreateViewHelloHolder方法中根据getItemViewHelloType返回的viewType判断需要初始化的布局。
在onCreateViewHelloHolder方法中创建holder的时候,可以自定义holder并继承HelloHolder,复写HelloHolder中的bindViewData方法,直接在自定义的holder中bindViewData方法里做数据绑定,这样在adapter样式比较复杂时,可以显得代码非常简洁清晰。
源码地址:github地址
HelloAdapter主要代码:
abstract class HelloAdapter<T>(var context: Context) : RecyclerView.Adapter<HelloHolder<T>>() {
// 显示数据的布局id
private var layoutId = 0
// 空数据要传的布局id
private var emptyLayoutId = 0
private var baseData: MutableList<T> = ArrayList<T>()
// 要监听的item上的viewid集合
private var listenerViewsIds: List<Int>? = null
// 自定义的一个String类型的数据
private var customData: String? = null
// 无数据时是否显示暂无数据提示页面
private var showEmptyLayout = true
private var onItemClickListener: OnItemClickListener? = null
private var onItemClickForDataListener: OnItemClickForDataListener<T>? = null
private var onItemLongClickListener: OnItemLongClickListener? = null
private var onItemLongClickForDataListener: OnItemLongClickForDataListener<T>? = null
private var onHeadAndFootClickListener: OnHeadAndFootClickListener? = null
// 头布局view
private val HEADER_VIEW = 0x01
// 尾布局view
private val FOOTER_VIEW = 0x02
// 数据填充的view
private val DATA_VIEW = 0x03
// 空数据显示的view
private val EMPTY_VIEW = 0x04
// 空数据的viewHolder
private var emptyHolder: HelloHolder<T>? = null
// 头部view的父布局
private var headerViewParentLayout: LinearLayout? = null
// 尾部view的父布局
private var footerViewParentLayout: LinearLayout? = null
override fun getItemCount(): Int {
// 增加 头、尾和空数据3个固定布局
return baseData.size + 3
}
override fun getItemViewType(position: Int): Int {
if (0 == position) {
return HEADER_VIEW
}
if (1 == position) {
return EMPTY_VIEW
}
return if (position < itemCount - 1) {
getItemViewHelloType(position - 2)
} else {
FOOTER_VIEW
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HelloHolder<T> {
when {
HEADER_VIEW == viewType -> {
if (null == headerViewParentLayout) {
initHeaderView()
} else {
val headerViewParent = headerViewParentLayout!!.parent
if (headerViewParent is ViewGroup) {
headerViewParent.removeView(headerViewParentLayout)
}
}
return HelloHolder(headerViewParentLayout!!)
}
EMPTY_VIEW == viewType -> {
val emptyLayout = if (emptyLayoutId == 0) {
R.layout.item_empty_layout_default
} else {
emptyLayoutId
}
val emptyView: View =
LayoutInflater.from(context).inflate(emptyLayout, parent, false)
emptyHolder = HelloHolder(emptyView)
return emptyHolder!!
}
FOOTER_VIEW == viewType -> {
if (null == footerViewParentLayout) {
initFooterView()
} else {
val footerViewParent = footerViewParentLayout!!.parent
if (footerViewParent is ViewGroup) {
footerViewParent.removeView(footerViewParentLayout)
}
}
return HelloHolder(footerViewParentLayout!!)
}
else -> {
return onCreateViewHelloHolder(parent, viewType)
}
}
}
override fun onBindViewHolder(holder: HelloHolder<T>, position: Int) {
try {
if (holder.itemViewType == EMPTY_VIEW && null != emptyHolder) { // 数据为空的布局
if (showEmptyLayout && baseData.size == 0) {
emptyHolder!!.setVisibility(true)
} else {
emptyHolder!!.setVisibility(false)
}
} else if (holder.itemViewType != HEADER_VIEW && holder.itemViewType != FOOTER_VIEW && holder.itemViewType != EMPTY_VIEW) {
val dataPosition = position - 2
if (dataPosition < baseData.size) {
bindViewHolder(holder, baseData, dataPosition)
setOnClick(holder.itemView, dataPosition)
setOnLongClick(holder.itemView, dataPosition)
if (null != listenerViewsIds && listenerViewsIds!!.isNotEmpty()) {
for (viewId in listenerViewsIds!!) {
val cv: View? = holder.getView(viewId)
if (cv != null) {
setOnClick(cv, dataPosition)
setOnLongClick(cv, dataPosition)
}
}
}
holder.bindViewData(baseData, dataPosition)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 多布局时,自定义布局类型
*/
open fun getItemViewHelloType(position: Int): Int {
return DATA_VIEW
}
/**
* 多布局时,通过viewType创建不同的HelloHolder
*/
open fun onCreateViewHelloHolder(parent: ViewGroup, viewType: Int): HelloHolder<T> {
val view = LayoutInflater.from(context).inflate(layoutId, parent, false)
return HelloHolder(view)
}
/**
* 设置data填充的布局
*/
open fun setLayoutId(layoutId: Int): HelloAdapter<T> {
this.layoutId = layoutId
return this
}
/**
* 设置空数据时显示的暂无数据布局(不设置显示默认布局)
*/
fun setEmptyLayoutId(emptyLayoutId: Int): HelloAdapter<T> {
this.emptyLayoutId = emptyLayoutId
return this
}
/**
* 数据data为空时,是否显示空数据的页面()
*/
fun showEmptyLayout(isShow: Boolean): HelloAdapter<T> {
showEmptyLayout = isShow
notifyDataSetChanged()
return this
}
/**
* 设置监听item上的view点击事件(传参view的id集合)
*/
fun setListenerViewsIds(listenerViewsIds: MutableList<Int>?): HelloAdapter<T> {
this.listenerViewsIds = listenerViewsIds
return this
}
/**
* 一个自定义的扩展字段String
*/
fun setCustomData(customData: String?): HelloAdapter<T> {
this.customData = customData
return this
}
/**
* 设置数据集合(覆盖原数据)
*/
open fun setData(data: MutableList<T>?): HelloAdapter<T> {
if (data != null) {
this.baseData = data
notifyDataSetChanged()
}
return this
}
/**
* 添加数据(在原数据基础上添加数据)
*
* @param data
*/
open fun addData(data: T) {
if (null != data) {
baseData.add(data)
}
notifyDataSetChanged()
}
/**
* 添加数据集(在原数据基础上添加数据)
* @param data
*/
open fun addData(data: MutableList<T>) {
baseData.addAll(data)
notifyDataSetChanged()
}
/**
* 获取所有的数据集合
*/
open fun getData(): MutableList<T> {
return baseData
}
/**
* 获取所有的尾布局中添加的子view
*/
open fun getAllFooterView(): Sequence<View>? {
return footerViewParentLayout?.children
}
/**
* 获取所有的头布局中添加的子view
*/
open fun getAllHeaderView(): Sequence<View>? {
return headerViewParentLayout?.children
}
/**
* 获取头布局中添加的子view数量
*/
open fun getHeaderViewCount(): Int? {
return headerViewParentLayout?.childCount
}
/**
* 获取尾布局中添加的子view数量
*/
open fun getFooterViewCount(): Int? {
return footerViewParentLayout?.childCount
}
/**
* 添加尾布局
*
* @param layoutId
* @return 添加尾的view
*/
open fun addFooterView(layoutId: Int): View {
return addFooterView(layoutId, -1)
}
/**
* 在指定位置添加尾布局
*
* @param layoutId
* @param index 放置尾布局view的下标
* @return 添加尾的view
*/
open fun addFooterView(layoutId: Int, index: Int): View {
if (null == footerViewParentLayout) {
initFooterView()
}
val foot =
LayoutInflater.from(context).inflate(layoutId, footerViewParentLayout, false)
footerViewParentLayout!!.addView(foot)
return foot
}
/**
* 添加尾布局并给尾布局的子view设置点击事件
*
* @param layoutId
* @return 添加尾的view
*/
open fun addFooterAndClickListener(layoutId: Int, viewIds: MutableList<Int>): View? {
val foot: View = addFooterView(layoutId)
for (viewId in viewIds) {
val view: View = foot.findViewById(viewId)
view.setOnClickListener {
if (null != onHeadAndFootClickListener) {
onHeadAndFootClickListener!!.headAndFootClick(view)
}
}
}
return foot
}
/**
* 添加头布局
*
* @param layoutId
* @return 添加头的view
*/
open fun addHeaderView(layoutId: Int): View {
return addHeaderView(layoutId, -1)
}
/**
* 在指定位置添加头布局
*
* @param layoutId
* @param index 放置头布局view的下标
* @return 添加头的view
*/
open fun addHeaderView(layoutId: Int, index: Int): View {
if (null == headerViewParentLayout) {
initHeaderView()
}
val header =
LayoutInflater.from(context).inflate(layoutId, headerViewParentLayout, false)
headerViewParentLayout!!.addView(header, index)
return header
}
/**
* 添加头布局并给头布局的子view设置点击事件
*
* @param layoutId
* @return 添加头的view
*/
open fun addHeaderAndClickListener(layoutId: Int, viewIds: MutableList<Int>): View {
val header = addHeaderView(layoutId)
for (viewId in viewIds) {
val view: View = header.findViewById(viewId)
view.setOnClickListener {
if (null != onHeadAndFootClickListener) {
onHeadAndFootClickListener!!.headAndFootClick(view)
}
}
}
return header
}
/**
* 移除指定的尾布局view
*
* @param view
*/
open fun removeFooterView(view: View?) {
if (null != footerViewParentLayout) {
footerViewParentLayout!!.removeView(view)
}
}
/**
* 移除指定的尾布局view
*
* @param viewIndex(添加的View下标值)
*/
open fun removeFooterView(viewIndex: Int?) {
if (null != footerViewParentLayout && null != viewIndex) {
footerViewParentLayout!!.removeViewAt(viewIndex)
}
}
/**
* 移除所有尾布局View
*/
open fun removeAllFooterView() {
if (null != footerViewParentLayout) {
footerViewParentLayout!!.removeAllViews()
}
}
/**
* 移除指定的头布局View
*
* @param view
*/
open fun removeHeaderView(view: View?) {
if (null != headerViewParentLayout) {
headerViewParentLayout!!.removeView(view)
}
}
/**
* 根据view下标移除头布局View
*
* @param viewIndex(添加的View下标值)
*/
open fun removeHeaderView(viewIndex: Int?) {
if (null != headerViewParentLayout && null != viewIndex) {
headerViewParentLayout!!.removeViewAt(viewIndex)
}
}
/**
* 移除所有的头布局View
*/
open fun removeAllHeaderView() {
if (null != headerViewParentLayout) {
headerViewParentLayout!!.removeAllViews()
}
}
/**
* 初始化头部布局
*/
private fun initHeaderView() {
headerViewParentLayout = LinearLayout(context)
headerViewParentLayout!!.orientation = LinearLayout.VERTICAL
val layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT,
RecyclerView.LayoutParams.WRAP_CONTENT
)
headerViewParentLayout!!.layoutParams = layoutParams
}
/**
* 初始化底部布局
*/
private fun initFooterView() {
if (null == footerViewParentLayout) {
footerViewParentLayout = LinearLayout(context)
footerViewParentLayout!!.orientation = LinearLayout.VERTICAL
val layoutParams = RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT,
RecyclerView.LayoutParams.WRAP_CONTENT
)
footerViewParentLayout!!.layoutParams = layoutParams
}
}
protected abstract fun bindViewHolder(
holder: HelloHolder<T>,
data: MutableList<T>,
position: Int
)
/**
* 设置头和尾view的监听事件
*/
fun setOnHeadAndFootClickListener(onHeadAndFootClick: OnHeadAndFootClickListener?) {
this.onHeadAndFootClickListener = onHeadAndFootClick
}
interface OnHeadAndFootClickListener {
fun headAndFootClick(view: View?)
}
interface OnItemClickListener {
fun itemClick(view: View?, position: Int)
}
interface OnItemClickForDataListener<T> {
fun itemClickForData(view: View?, position: Int, data: T)
}
interface OnItemLongClickListener {
fun itemLongClick(view: View?, position: Int)
}
interface OnItemLongClickForDataListener<T> {
fun itemLongClickForData(view: View?, position: Int, data: T)
}
fun setOnItemClickListener(onItemClickListener: OnItemClickListener?) {
this.onItemClickListener = onItemClickListener
}
fun setOnItemClickForDataListener(onItemClickForDataListener: OnItemClickForDataListener<T>) {
this.onItemClickForDataListener = onItemClickForDataListener
}
fun setOnItemLongClickListener(onItemLongClickListener: OnItemLongClickListener?) {
this.onItemLongClickListener = onItemLongClickListener
}
fun setOnItemLongClickForDataListener(onItemLongClickForDataListener: OnItemLongClickForDataListener<T>) {
this.onItemLongClickForDataListener = onItemLongClickForDataListener
}
private fun setOnClick(view: View, position: Int) {
view.setOnClickListener {
try {
if (null != onItemClickListener) {
onItemClickListener!!.itemClick(view, position)
}
if (null != onItemClickForDataListener) onItemClickForDataListener!!.itemClickForData(
view,
position,
baseData[position]
)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun setOnLongClick(v: View, position: Int) {
v.setOnLongClickListener {
try {
if (null != onItemLongClickListener) {
onItemLongClickListener!!.itemLongClick(v, position)
}
if (null != onItemLongClickForDataListener) {
onItemLongClickForDataListener!!.itemLongClickForData(
v,
position,
baseData[position]
)
}
} catch (e: Exception) {
e.printStackTrace()
}
true
}
}
}
继承HelloAdapter,实现自己的adapter。
class TestAdapter(context: Context) : HelloAdapter<String>(context) {
private val LAYOUT1 = 10
private val LAYOUT2 = 20
// 当adapter为多布局时可以复写该方法
override fun getItemViewHelloType(position: Int): Int {
if (position % 2 == 0) {
return LAYOUT1
} else if (position == 3 || position == 5 || position == 7 || position == 9){
return LAYOUT2
}
return super.getItemViewHelloType(position)
}
// 当adapter为多布局时可以复写该方法
override fun onCreateViewHelloHolder(parent: ViewGroup, viewType: Int): HelloHolder<String> {
// 当adapter为多布局时,根据getItemViewHelloType返回的viewType来初始化不同布局和不同holder,数据绑定可在每个holder中做处理。
if (viewType == LAYOUT1) {
val view = LayoutInflater.from(context).inflate(R.layout.holder_layout1, parent, false)
return LayoutHolder1(view)
} else if (viewType == LAYOUT2) {
val view = LayoutInflater.from(context).inflate(R.layout.holder_layout2, parent, false)
return LayoutHolder2(view)
}
return super.onCreateViewHelloHolder(parent, viewType)
}
override fun bindViewHolder(holder: HelloHolder<String>, data: MutableList<String>, position: Int) {
// 当adapter中为单一布局时,可以在此实现数据绑定。
}
}
LayoutHolder1中在bindViewData方法中做数据绑定:
class LayoutHolder1(itemView: View) : HelloHolder<String>(itemView) {
override fun bindViewData(data: MutableList<String>, position: Int) {
super.bindViewData(data, position)
setItemBackgroudColor(Color.parseColor("#edea23"))
this.itemView.holder1_item_layout_tv.text = "holder1:"+data[position]
}
}
在Activity中初始化自己的adapter,并实现HelloAdapter.OnHeadAndFootClickListener接口,监听头和尾子view的点击事件。
adapter = TestAdapter(this)
.setLayoutId(R.layout.item_layout)
.setData(data)
.setEmptyLayoutId(R.layout.item_empty_layout_a)
.showEmptyLayout(true)
// val header: View = adapter?.addHeaderView(R.layout.header_layout3)!!
var layoutIds: MutableList<Int> = arrayListOf()
layoutIds.add(R.id.head_bt1)
layoutIds.add(R.id.head_bt2)
layoutIds.add(R.id.head_bt3)
val header: View = adapter?.addHeaderAndClickListener(R.layout.header_layout3,layoutIds)!!
adapter?.setOnItemClickForDataListener(this)
adapter?.setOnHeadAndFootClickListener(this)
override fun headAndFootClick(view: View?) {
when(view?.id){
R.id.head_bt1 -> {
Toast.makeText(this,"我是button1",Toast.LENGTH_SHORT).show()
}
R.id.head_bt2 -> {
Toast.makeText(this,"我是button2",Toast.LENGTH_SHORT).show()
}
R.id.head_bt3 -> {
Toast.makeText(this,"我是button3",Toast.LENGTH_SHORT).show()
}
}
}
本文地址:https://blog.csdn.net/u014333053/article/details/108281923
上一篇: 八戒女儿的餐巾纸
下一篇: 小丽与阿强 他们的关系发展得很迅速