美文网首页
RecyclerView简单的粘性头部实现--选择汽车品牌为例

RecyclerView简单的粘性头部实现--选择汽车品牌为例

作者: 假装程序猿 | 来源:发表于2021-01-17 14:41 被阅读0次

1、使用ItemDecoration

class CarBrandStickItemDecoration(var context: Context) : RecyclerView.ItemDecoration() {

    private var mItemHeaderHeight = 50
    private var mItemHeaderPaint = Paint()
    private var mTextPaint = Paint()
    private var mLinePaint = Paint()
    private var hotCityText = "热门品牌"

    init {
        mItemHeaderHeight = context.dp2px(30)
        mItemHeaderPaint.color = context.resources.getColor(R.color.color_gray_f0)
        mTextPaint.color = context.resources.getColor(R.color.color_gray_ae)
        mTextPaint.textSize = context.sp2px(14).toFloat()
        mLinePaint.color = context.resources.getColor(R.color.color_gray_eb)
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.adapter is CarBrandAdapter) {
            val adapter = parent.adapter as CarBrandAdapter
            if (adapter.itemCount == 0) {
                return
            }
            val position = parent.getChildAdapterPosition(view)
            if (adapter.isHeaderItem(position)) {
                outRect.top = mItemHeaderHeight
            } else {
                outRect.top = 1
            }
        }
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.adapter is CarBrandAdapter) {
            val adapter = parent.adapter as CarBrandAdapter
            if (adapter.itemCount == 0) {
                return
            }
            for (i in 0 until parent.childCount) {
                val view = parent.getChildAt(i)
                val position = parent.getChildAdapterPosition(view)
                if (adapter.isHeaderItem(position)) {
                    c.drawRect(0f, (view.top - mItemHeaderHeight).toFloat(), parent.width.toFloat(), view.top.toFloat(), mItemHeaderPaint)
                    val textRect = Rect()
                    if (adapter.hasHotCity() && position == 0) {
                        mTextPaint.getTextBounds(hotCityText, 0, hotCityText.length, textRect)
                        c.drawText(hotCityText, context.dp2px(15).toFloat(),
                                ((view.top - mItemHeaderHeight) + mItemHeaderHeight / 2 + textRect.height() / 2).toFloat(), mTextPaint)
                    } else {
                        mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length, textRect)
                        c.drawText(adapter.getGroupName(position), context.dp2px(15).toFloat(),
                                ((view.top - mItemHeaderHeight) + mItemHeaderHeight / 2 + textRect.height() / 2).toFloat(), mTextPaint)
                    }
                } else {
                    c.drawRect(0f, view.top - 1f, parent.width.toFloat(), view.top.toFloat(), mLinePaint)
                }
            }
        }
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        if (parent.adapter is CarBrandAdapter) {
            val adapter = parent.adapter as CarBrandAdapter
            if (adapter.itemCount == 0) {
                return
            }
            val position = (parent.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
            val view = parent.findViewHolderForAdapterPosition(position)?.itemView!!
            if (adapter.isHeaderItem(position + 1)) {
                val bottom = Math.min(mItemHeaderHeight, view.bottom)
                c.drawRect(0f, view.top - mItemHeaderHeight.toFloat(), parent.width.toFloat(), bottom.toFloat(), mItemHeaderPaint)
                val textRect = Rect()
                if (adapter.hasHotCity() && position == 0) {
                    mTextPaint.getTextBounds(hotCityText, 0, hotCityText.length, textRect)
                    c.drawText(hotCityText, context.dp2px(15).toFloat(), mItemHeaderHeight.toFloat() / 2 + textRect.height() / 2 - (mItemHeaderHeight - bottom), mTextPaint)
                } else {
                    mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length, textRect)
                    c.drawText(adapter.getGroupName(position), context.dp2px(15).toFloat(), mItemHeaderHeight.toFloat() / 2 + textRect.height() / 2 - (mItemHeaderHeight - bottom), mTextPaint)
                }
            } else {
                c.drawRect(0f, 0f, parent.width.toFloat(), mItemHeaderHeight.toFloat(), mItemHeaderPaint)
                val textRect = Rect()
                if (adapter.hasHotCity() && position == 0) {
                    mTextPaint.getTextBounds(hotCityText, 0, hotCityText.length, textRect)
                    c.drawText(hotCityText, context.dp2px(15).toFloat(), mItemHeaderHeight.toFloat() / 2 + textRect.height() / 2, mTextPaint)
                } else {
                    mTextPaint.getTextBounds(adapter.getGroupName(position), 0, adapter.getGroupName(position).length, textRect)
                    c.drawText(adapter.getGroupName(position), context.dp2px(15).toFloat(), mItemHeaderHeight.toFloat() / 2 + textRect.height() / 2, mTextPaint)
                }
            }
        }
    }
}

2、adapter

class CarBrandAdapter(var context: Context, var mList: MutableList<CarBrand>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private var hotList = mutableListOf<CarBrand>()
    var layoutManager: LinearLayoutManager? = null
    private var onCarBrandSelectedListener: OnCarBrandSelectedListener?= null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        if (viewType == 0)
            return BrandViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_item_car_brand, parent, false))
        return HotBrandViewHolder(LayoutInflater.from(context).inflate(R.layout.layout_hot_brand, parent, false))
    }

    override fun getItemCount(): Int {
        if (hotList.isEmpty()) {
            return mList.size
        }
        return mList.size + 1
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        if (holder is BrandViewHolder) {
            val realPosition = getRealPosition(position)
            if (mList[realPosition].carIcon.isNotNil()) {
                holder.ivLogo.display(mList[realPosition].carIcon)
            } else {
                holder.ivLogo.setImageResource(R.mipmap.default_product)
            }
            holder.tvBrand.text = mList[realPosition].brandName
            holder.clContent.onClick {
                onCarBrandSelectedListener?.apply {
                    this.onCarBrandSelected(mList[realPosition])
                }
            }
        } else if (holder is HotBrandViewHolder) {
            holder.rvHotBrand.layoutManager = GridLayoutManager(context, 5)
            val adapter = BaseRecyclerViewAdapter(context, hotList, R.layout.layout_tab_item_5_1) { view, carBrand, position1 ->
                if (carBrand.carIcon.isNotNil()) {
                    view.findViewById<ImageView>(R.id.iv_icon).display(carBrand.carIcon)
                } else {
                    view.findViewById<ImageView>(R.id.iv_icon).setImageResource(R.mipmap.default_product)
                }
                view.findViewById<TextView>(R.id.tv_text).text = carBrand.brandName
            }
            adapter.setOnItemClickListener(object : BaseRecyclerViewAdapter.OnItemClickListener {
                override fun onItemClick(v: View, position: Int) {
                    onCarBrandSelectedListener?.apply {
                        this.onCarBrandSelected(hotList[position])
                    }
                }
            })
            holder.rvHotBrand.adapter = adapter
        }
    }

    override fun getItemViewType(position: Int): Int {
        if (position == 0 && hotList.isNotEmpty()) {
            return 1
        }
        return 0
    }

    fun scrollToSection(index: String) {
        if (index == "#" && hotList.isNotEmpty() && layoutManager != null) {
            layoutManager?.scrollToPositionWithOffset(0, 0)
            return
        }
        if (mList.isEmpty()) return
        if (index.isEmpty()) return
        for (i in 0 until mList.size) {
            if (index == mList[i].headChar.substring(0, 1)) {
                if (layoutManager != null) {
                    layoutManager?.scrollToPositionWithOffset(i, 0)
                    return
                }
            }
        }
    }

    fun isHeaderItem(position: Int): Boolean {
        if (position == 0) {
            return true
        } else {
            val realPosition = getRealPosition(position)

            val lastGroupName = if (realPosition == 0) "#" else mList[realPosition - 1].headChar.substring(0, 1)
            val currGroupName = mList[realPosition].headChar.substring(0, 1)
            return lastGroupName != currGroupName
        }
    }

    fun getGroupName(position: Int): String {
        if (position == 0 && hotList.isNotEmpty()) {
            return hotList[0].headChar.substring(0, 1)
        }
        return mList[getRealPosition(position)].headChar.substring(0, 1)
    }

    fun getRealPosition(position: Int): Int {
        return if (itemCount == mList.size) position else position - 1
    }

    fun hasHotCity(): Boolean {
        return hotList.isNotEmpty()
    }

    fun setData(data: List<CarBrand>, hotList: List<CarBrand>) {
        mList.clear()
        mList.addAll(data)
        this.hotList.clear()
        this.hotList.addAll(hotList)
        notifyDataSetChanged()
    }

    fun setOnCarBrandSelectedLsitener(onCarBrandSelectedListener: OnCarBrandSelectedListener) {
        this.onCarBrandSelectedListener = onCarBrandSelectedListener
    }

    class BrandViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var clContent = itemView.findViewById<ConstraintLayout>(R.id.cl_content)
        var ivLogo = itemView.findViewById<ImageView>(R.id.iv_logo)
        var tvBrand = itemView.findViewById<TextView>(R.id.tv_brand)
    }

    class HotBrandViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var rvHotBrand = itemView.findViewById<RecyclerView>(R.id.rv_hot)
    }

    interface OnCarBrandSelectedListener {
        fun onCarBrandSelected(brand: CarBrand)
    }
}

3、数据结构

data class CarBrand(
        var brandName: String = "",
        var id: String,
        var carIcon: String = "",
        var headChar: String = "",
        var pinYin: String = ""
)

相关文章

网友评论

      本文标题:RecyclerView简单的粘性头部实现--选择汽车品牌为例

      本文链接:https://www.haomeiwen.com/subject/iiiaaktx.html