Android RecyclerView 吸顶效果

Posted danfengw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android RecyclerView 吸顶效果相关的知识,希望对你有一定的参考价值。

请先阅读Android RecyclerView 吸顶效果(一),推荐参考第一篇文章的方案来实现,这里只是帖一下第二种方案的代码,简单的介绍一下ItemDecoration

实现效果:

ItemDecoration


太懒了,截个图,如有侵权,请告知,会进行删除!

主要代码

StickyItemDecoration

class StickyItemDecoration(val context: Context, val mList: MutableList<Bean> = mutableListOf()) :
    RecyclerView.ItemDecoration() 

    private var paint: Paint? = null
    private var textPaint: Paint = Paint()
    private var dividerHeight: Float = dp2Px(50).toFloat()

    init 
        paint = Paint()
        paint?.setColor(context.resources.getColor(R.color.colorAccent))
        paint?.style = Paint.Style.FILL

        textPaint?.setColor(context.resources.getColor(R.color.FF333333))
        textPaint?.style = Paint.Style.FILL
        textPaint?.textSize = sp2Px(14).toFloat()
    

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) 
        super.getItemOffsets(outRect, view, parent, state)
        val position = parent.getChildAdapterPosition(view)
        if (isGroupFirst(position)) 
            outRect.top = dividerHeight.toInt()
         else 
            outRect.top = 0
        
    

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) 
        super.onDraw(canvas, parent, state)
        val childCount = parent.childCount
        for (i in 0 until childCount) 
            val childView = parent.getChildAt(i)
            val childAdapterPos = parent.getChildAdapterPosition(childView)
            val groupName = getGroupName(childAdapterPos)
            val left = childView.paddingLeft
            val right = childView.paddingRight
            paint?.let 
                if (isGroupFirst(childAdapterPos)) 
                    val top = childView.top - dividerHeight
                    val bottom = childView.top.toFloat()
                    canvas.drawRect(left.toFloat(), top, (childView.width - right).toFloat(), bottom, it)
                    groupName ?: return
                    val baseLine = (top + bottom) / 2f - (textPaint.descent() + textPaint.ascent()) / 2f
                    canvas.drawText(groupName, left + dp2Px(10).toFloat(), baseLine, textPaint)
                
            
        
    

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) 
        super.onDrawOver(canvas, parent, state)
        val firstVisiblePos = (parent.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
        // 这里必须获取itemView 不能通过parent.getChildAt的方式,原因是getChildAt的方式会出现top准确的情况
        val view = parent.findViewHolderForAdapterPosition(firstVisiblePos)?.itemView
        view ?: return
        val top = parent.paddingTop
        val left = parent.paddingLeft
        val right = parent.width - view.paddingRight
        paint?.let 
            if (isGroupFirst(firstVisiblePos)) 
                val bottom = Math.min(dividerHeight, view.bottom.toFloat())
                canvas.drawRect(left.toFloat(), top + view.top - dividerHeight, right.toFloat(), top + bottom, it)
                val baseLine = ((top + bottom) - (textPaint.ascent() + textPaint.descent())) / 2f
                canvas.drawText(getGroupName(firstVisiblePos)!!, left + dp2Px(10).toFloat(), baseLine, textPaint)
             else 
                canvas.drawRect(left.toFloat(), top.toFloat(), right.toFloat(), top.toFloat() + dividerHeight, it)
                val baseLine = ((top + top + dividerHeight) - (textPaint.ascent() + textPaint.descent())) / 2f
                canvas.drawText(getGroupName(firstVisiblePos)!!, left + dp2Px(10).toFloat(), baseLine, textPaint)
            
        
    

    private fun getGroupName(position: Int): String? 
        return mList[position].groupName
    

    private fun isGroupFirst(position: Int): Boolean 
        return if (position == 0) 
            true
         else 
            val lastGroupName = mList[position - 1].groupName
            val currentGroupName = mList[position].groupName
            lastGroupName != currentGroupName
        
    

    private fun dp2Px(dpValue: Int): Int 
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue.toFloat(), context.resources.displayMetrics).toInt()
    

    private fun sp2Px(spValue: Int): Int 
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue.toFloat(), context.resources.displayMetrics).toInt()
    

数据

class Bean(val text: String, val groupName: String)

Activity

class SimpleActivity : AppCompatActivity() 
    private val beanList = mutableListOf<Bean>()
    private var recyclerView: RecyclerView? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample_2)
        initData()
        initRecyclerView()
    

    private fun initRecyclerView() 
        recyclerView = findViewById(R.id.recyclerView)
        recyclerView?.adapter = SampleAdapter2(beanList)
        recyclerView?.layoutManager = LinearLayoutManager(this)
        recyclerView?.layoutParams?.width = ViewGroup.LayoutParams.MATCH_PARENT
        recyclerView?.layoutParams?.height = ViewGroup.LayoutParams.WRAP_CONTENT
        recyclerView?.addItemDecoration(StickyItemDecoration(this, beanList))
    

    private fun initData() 
        for (i in 0..5) 
            beanList.add(Bean("第一组 $i + 1 号", "第一组"))
        
        for (i in 0..5) 
            beanList.add(Bean("第二组 $i + 1 号", "第二组"))
        
        for (i in 0..5) 
            beanList.add(Bean("第三组 $i + 1 号", "第三组"))
        
        for (i in 0..5) 
            beanList.add(Bean("第四组 $i + 1 号", "第四组"))
        
        for (i in 0..5) 
            beanList.add(Bean("第五组 $i + 1 号", "第五组"))
        
    

Adapter

class SampleAdapter2(var data: MutableList<Bean>? = mutableListOf()) : RecyclerView.Adapter<Holder>() 

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder 
        return Holder(LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false))
    

    override fun getItemCount(): Int 
        return data?.size ?: 0
    

    override fun onBindViewHolder(holder: Holder, position: Int) 
        holder.bindData(data?.get(position)?.text)
    


class Holder(view: View) : RecyclerView.ViewHolder(view) 
    private var textView: TextView? = null

    init 
        textView = view.findViewById(R.id.tv)
    

    fun bindData(s: String?) 
        s ?: return
        textView?.text = s
    

以上是关于Android RecyclerView 吸顶效果的主要内容,如果未能解决你的问题,请参考以下文章

Android 最流行的吸顶效果的实现及代码

Android 自定义ItemDecoration-实现分组吸顶效果

RecyclerView 悬浮吸顶效果实现,支持数据绑定及Touch事件

RecyclerView 悬浮吸顶效果实现,支持数据绑定及Touch事件

RecyclerView 悬浮吸顶效果实现,支持数据绑定及Touch事件

RecyclerView高级用法:分组+吸顶效果实现