RecyclerView创建多个Header(多布局)简记

290 阅读3分钟

实现头部多样式的布局方案网上有很多,这里简单记录下实现的方案:

import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView

/**
 * 支持添加header的Adapter
 */
open class HeaderAndFooterWrapperAdapter<H : HeaderAndFooterWrapperViewHolder, D : Any> :RecyclerView.Adapter<HeaderAndFooterWrapperViewHolder>() {

    private var headerViewMap: HashMap<Int,View> = hashMapOf()
    private var headerKey = 100000
    private var footerViewMap: HashMap<Int,View> = hashMapOf()
    private var footerKey = 200000
    private var contentAdapter: ContentAdapter<H, D>? = null

    fun addHeader(headerView: View): HeaderAndFooterWrapperAdapter<H,D> {
        headerViewMap.put(headerViewMap.size + headerKey,headerView)
        return this
    }

    fun addFooter(footerView: View): HeaderAndFooterWrapperAdapter<H,D> {
        footerViewMap.put(footerViewMap.size + footerKey,footerView)
        return this
    }

    fun setContentAdapter(contentAdapter: ContentAdapter<H,D>?): HeaderAndFooterWrapperAdapter<H,D> {
        this.contentAdapter = contentAdapter
        return this
    }

    fun getHeaderViews(): List<View> {
        return headerViewMap.values.toList()
    }

    fun getFooterViews(): List<View> {
        return footerViewMap.values.toList()
    }

    private fun getHeaderCount(): Int {
        return headerViewMap.size
    }

    private fun getFooterCount(): Int {
        return footerViewMap.size
    }

    private fun getContentCount(): Int{
        return contentAdapter?.itemCount?: 0
    }

    fun getContentAdapter(): ContentAdapter<H,D>? {
        return contentAdapter
    }

    open fun isHeaderViewPos(position: Int): Boolean {
        return position < getHeaderCount()
    }

    open fun isFooterViewPos(position: Int): Boolean {
        return position >= getHeaderCount() + getContentCount()
    }

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): HeaderAndFooterWrapperViewHolder {
        if (headerViewMap.containsKey(viewType)) {
            return HeaderAndFooterWrapperViewHolder(headerViewMap[viewType]!!)
        }else if(footerViewMap.containsKey(viewType)){
            return HeaderAndFooterWrapperViewHolder(footerViewMap[viewType]!!)
        }
        return contentAdapter!!.createViewHolder(parent,viewType)
    }

    override fun onBindViewHolder(holder: HeaderAndFooterWrapperViewHolder, position: Int) {
        if (isHeaderViewPos(position)){
            return
        }else if (isFooterViewPos(position)){
            return
        }
        contentAdapter?.onBindViewHolder(holder as H,position - getHeaderCount())
    }

    override fun getItemCount(): Int {
        return getHeaderCount() + getContentCount() + getFooterCount()
    }

    override fun getItemViewType(position: Int): Int {
        if (isHeaderViewPos(position)) {
            return headerViewMap.keys.toList()[position]
        }
        if (isFooterViewPos(position)) {
            return footerViewMap.keys.toList()[position - getHeaderCount() - getContentCount()]
        }
        return 0
    }
}

open class HeaderAndFooterWrapperViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {}
open abstract class ContentAdapter<H: HeaderAndFooterWrapperViewHolder,D> : RecyclerView.Adapter<H>() {

    //数据
    protected var dataList: MutableList<D> = mutableListOf()

    fun setDatas(data: MutableList<D>) {
        this.dataList.addAll(data)
    }

    fun getDatas(): MutableList<D> {
        return dataList
    }

    override fun getItemCount(): Int {
        return dataList.size
    }
}

使用方式:

headerAndFooterWrapperAdapter = HeaderAndFooterWrapperAdapter<ContentViewHolder, String>().apply {
        //添加header
        addHeader(Form4(context, null).buildApply {
            leftText = "测试"
            rightText = "文案"
        })
        addHeader(Form4(context, null).buildApply {
            leftText = "测试2"
            rightText = "文案2"
        })
        addHeader(Form4(context, null).buildApply {
            leftText = "测试3"
            rightText = "文案3"
        })
        //添加footer
        addFooter(Form4(context, null).buildApply {
            leftText = "测试4"
            rightText = "文案4"
        })
        //设置内容区域的Adapter
        setContentAdapter(object : ContentAdapter<ContentViewHolder, String>() {
            private var items = mutableListOf<String>()

            init {
                for (i in 0..100) {
                    items.add("== " + i.toString())
                }
                setDatas(items)
            }

            override fun onCreateViewHolder(
                parent: ViewGroup,
                viewType: Int
            ): ContentViewHolder {
                return ContentViewHolder(
                    LayoutInflater.from(parent.context)
                        .inflate(R.layout.item_ware_test, parent, false)
                )
            }

            override fun onBindViewHolder(
                holder: ContentViewHolder,
                position: Int
            ) {
                holder.tv_name.text = "商品名:${dataList.get(position)}"
                holder.tv_delete.setOnClickListener {
                    dataList.removeAt(position)
                    headerAndFooterWrapperAdapter?.notifyDataSetChanged()
                }
                holder.tv_add.setOnClickListener {
                    dataList.add("new Data $position")
                    headerAndFooterWrapperAdapter?.notifyDataSetChanged()
                }
            }
        })
    }
    //外部刷新头部
    bt_change_header.setOnClickListener {
        (ware_rv.adapter as HeaderAndFooterWrapperAdapter<ContentViewHolder,String>).apply {
            getHeaderViews().forEach { view ->  
                view as Form4
                view.refreshFormApply {
                    leftText += "new"+System.currentTimeMillis()
                    rightText += "new"+System.currentTimeMillis()
                    rightBottomText += "底部文案" +System.currentTimeMillis()
                }
            }
        }
    }
    //外部数据修改
    bt_change_rv.setOnClickListener {
        (ware_rv.adapter as HeaderAndFooterWrapperAdapter<ContentViewHolder,String>).apply {
            getContentAdapter()?.apply {
                getDatas().removeAt(0)
            }
            notifyDataSetChanged()
        }
    }
    //和RecyclerView绑定
    ware_rv.apply {
        layoutManager = LinearLayoutManager(context)
        adapter = headerAndFooterWrapperAdapter
    }
}

//外部内容部分的ViewHolder
class ContentViewHolder(itemView: View) : HeaderAndFooterWrapperViewHolder(itemView){
    var tv_name = itemView.findViewById<TextView>(R.id.tv_name)
    var tv_delete = itemView.findViewById<TextView>(R.id.tv_delete)
    var tv_add = itemView.findViewById<TextView>(R.id.tv_add)
}
好处:不用关心header和footer的位置
  • 方案2:使用ContactAdapter

//组合Adapter,可用于多布局
// setIsolateViewTypes 是否隔离子Adapter的ViewType true:表示隔离  false:表示不隔离
//当子AAdapter和BAdapter的ViewType返回一样时候
//true:表示隔离,AAdapter返回的是AAdapter绑定的ViewHolder,BAdapter返回的是BAdapter绑定的ViewHolder
//false:表示不隔离,有可能,BAdapter返回的可能是AAdapter绑定的ViewHolder
ware_rv.apply {
        layoutManager = LinearLayoutManager(context)
        adapter = ConcatAdapter(ConcatAdapter.Config.Builder().setIsolateViewTypes(true).build()).apply {
            addAdapter(ContentAdapter1())
            addAdapter(ContentAdapter2())
            addAdapter(ContentAdapter3())
            addAdapter(ContentAdapter1())
            addAdapter(ContentAdapter2())
            addAdapter(ContentAdapter3())
        }
    }


class ContentAdapter1() : RecyclerView.Adapter<ContentViewHolder>() {

    private var items = mutableListOf<String>()

    //
    init {
        for (i in 0..10) {
            items.add("ContentAdapter1 == " + i.toString())
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
        return ContentViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_ware_test, parent, false)
        )
    }

    override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
        holder.tv_name.text = "商品名:${items.get(position)}"
        holder.tv_delete.setOnClickListener {
            items.removeAt(position)
            notifyDataSetChanged()
        }
        holder.tv_add.setOnClickListener {
            items.add("ContentAdapter1 new Data $position")
            notifyDataSetChanged()
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

}

class ContentAdapter2() : RecyclerView.Adapter<ContentViewHolder>() {

    private var items = mutableListOf<String>()

    //
    init {
        for (i in 0..10) {
            items.add("ContentAdapter2 == " + i.toString())
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
        return ContentViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.item_ware_test_2, parent, false)
        )
    }

    override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
        holder.tv_name.text = "商品名:${items.get(position)}"
        holder.tv_delete.setOnClickListener {
            items.removeAt(position)
            notifyDataSetChanged()
        }
        holder.tv_add.setOnClickListener {
            items.add("ContentAdapter2 new Data $position")
            notifyDataSetChanged()
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

}
class ContentAdapter3() : RecyclerView.Adapter<ContentViewHolder>() {

    private var items = mutableListOf<String>()

    //
    init {
        for (i in 0..10) {
            items.add("ContentAdapter3 == " + i.toString())
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContentViewHolder {
        return ContentViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.item_ware_test_3, parent, false)
        )
    }

    override fun onBindViewHolder(holder: ContentViewHolder, position: Int) {
        holder.tv_name.text = "商品名:${items.get(position)}"
        holder.tv_delete.setOnClickListener {
            items.removeAt(position)
            notifyDataSetChanged()
        }
        holder.tv_add.setOnClickListener {
            items.add("ContentAdapter3 new Data $position")
            notifyDataSetChanged()
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

}

//
class ContentViewHolder(itemView: View) : HeaderAndFooterWrapperViewHolder(itemView) {
    var tv_name = itemView.findViewById<TextView>(R.id.tv_name)
    var tv_delete = itemView.findViewById<TextView>(R.id.tv_delete)
    var tv_add = itemView.findViewById<TextView>(R.id.tv_add)
}

好处:可以很方便的实现多布局,还有许多细节,读者自行研究

  • 方案3,自定义View控制滑动事件和Header\Footer的布局

待续----