RecyclerView 吸顶实现

借助RecyclerView 的 ItemDecoration

ItemDecoration 允许应用给具体的View添加具体的图画或者layout的偏移,对于绘制View之间的分割线,视觉分组边界等等是非常有用的。

当我们调用addItemDecoration()方法添加decoration的时候,RecyclerView就会调用该类的onDraw方法去绘制分隔线,也就是说:分隔线是绘制出来的。

RecyclerView.ItemDecoration,该类为抽象类,官方目前只提供了一个实现类DividerItemDecoration。

三个方法

onDraw
onDrawOver
getItemOffsets

绘制顺序

onDraw------itemView----onDrawOver

源码

我用kotlin实现

StarDecoration 分割线

class StarDecoration(val context: Context) : RecyclerView.ItemDecoration() {
    private val groupHeaderHeight: Int = dp2px(context,
            100f)
    private val headPaint: Paint = Paint()
    private val textPaint: Paint = Paint()
    private val textRect: Rect = Rect()

    init {
        headPaint.color = Color.RED
        textPaint.textSize = 50f
        textPaint.color = Color.WHITE
    }

    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDraw(c, parent, state)
        if (parent.adapter is StarAdapter) {
            val starAdapter = parent.adapter as StarAdapter
            val left = parent.paddingLeft
            val right = parent.width - parent.paddingRight
            var count = parent.childCount
            for (i in 0 until count) {
                // curView
                val view = parent.getChildAt(i)
                val position = parent.getChildAdapterPosition(view)
                val isGroupHead = starAdapter.isGroupHeader(position)
                if (isGroupHead && view.top - groupHeaderHeight - parent.paddingTop >= 0) {
                    c.drawRect(left.toFloat(), (view.top - groupHeaderHeight).toFloat(), right.toFloat(), view.bottom.toFloat(), headPaint)
                    val groupName = starAdapter.getGroupName(position)
                    textPaint.getTextBounds(groupName, 0, groupName.length, textRect);
                    c.drawText(groupName, (left + 20).toFloat(),
                            (view.top - groupHeaderHeight / 2 + textRect.height() / 2).toFloat(), textPaint)
                } else if(view.top - groupHeaderHeight - parent.paddingTop >= 0){
                    c.drawRect(left.toFloat(), (view.top - 4).toFloat(), right.toFloat(), view.top.toFloat(), headPaint);
                }

            }
        }
    }

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        super.onDrawOver(c, parent, state)
        if (parent.adapter is StarAdapter) {
            val starAdapter = parent.adapter as StarAdapter
            val position = (parent.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
            val itemView: View? = parent.findViewHolderForAdapterPosition(position)?.itemView
            val left = parent.paddingLeft
            val right = parent.width - parent.paddingRight
            val top = parent.paddingTop
            val isGroupHead = starAdapter.isGroupHeader(position + 1)
            if (isGroupHead) {
                val groupName = starAdapter.getGroupName(position)
                val bottom: Int = Math.min(groupHeaderHeight, itemView?.bottom
                        ?: 0 - parent.paddingTop)
                c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(),
                        (bottom + top).toFloat(), headPaint)
                textPaint.getTextBounds(groupName, 0, groupName.length, textRect);
                c.drawText(groupName, (left + 20).toFloat(),
                        (top - groupHeaderHeight / 2 + textRect.height() / 2 + bottom).toFloat(),
                        textPaint)

            } else {
                val groupName = starAdapter.getGroupName(position)
                c.drawRect(left.toFloat(), top.toFloat(), right.toFloat(),
                        (top + groupHeaderHeight).toFloat(), headPaint)
                textPaint.getTextBounds(groupName, 0, groupName.length, textRect);
                c.drawText(groupName, (left + 20).toFloat(),
                        (top + groupHeaderHeight / 2 + textRect.height() / 2).toFloat(), textPaint)
            }

        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)
        if (parent.adapter is StarAdapter) {
            val starAdapter = parent.adapter as StarAdapter
            val position: Int = parent.getChildLayoutPosition(view)
            val isGroupHeader = starAdapter.isGroupHeader(position)
            if (isGroupHeader) {
                outRect.set(0, groupHeaderHeight, 0, 0)
            } else {
                outRect.set(0, 0, 4, 0)
            }

        }
    }
}

入口Activity

class RecyclerViewActivity : AppCompatActivity() {
    val starList = mutableListOf()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_recycler_view)
        initData()
        rv_list.layoutManager = LinearLayoutManager(this)
        rv_list.addItemDecoration(StarDecoration(this))
        rv_list.adapter = StarAdapter(starList,this)

    }

    private fun initData() {

        for(i in 0 until 4){
            for(j in 0 until 20){
                if(i % 2 == 0){
                    starList.add(Star("何XXA$j","快乐家族$i"))
                }else {
                    starList.add(Star("汪XXA$j","天天兄弟$i"))

                }
            }
        }
    }
}

布局


    


Adapter & item



class StarAdapter(val starList: MutableList, val context: Context) : RecyclerView.Adapter() {


    // TODO: 2020/7/22 inner class & class ??????
    class StarViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        var tv: TextView = itemView.findViewById(R.id.  iv_start)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StarViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.rv_item_star, null)
        return StarViewHolder(view)
    }

    override fun getItemCount(): Int = starList.size

    override fun onBindViewHolder(holder: StarViewHolder, position: Int) {
        holder.tv.text = starList[position].name
    }

    fun isGroupHeader(position: Int): Boolean {
        return if (position == 0)
            true
        else {
            val curGroupName = getGroupName(position)
            val preGroupName = getGroupName(position -1)
            return preGroupName != curGroupName
        }
    }

    fun getGroupName(position: Int): String {
        return 
            starList[position].groupName
    }
}



    


原理

拿到分割线,分割先可以有多个

addItemDecoration 调用了requestLayout

之后会计算

measureChildWithMargins->getItemDecorInsetsForChild->getItemOffsets

我们的RecyclerView 的onDraw方法说明了绘制的顺序

draw()->super.onDraw()->mItemDecorations.get(i).onDraw

draw()->mItemDecorations.get(i).onDrawOver()

你可能感兴趣的:(RecyclerView 吸顶实现)