Kotlin--›Android 超輕,超好用,超簡潔,超超超級RecyclerView分割線ItemDecoration封裝

在這裏插入圖片描述

需求分析

如圖所示的:

  1. 101(共2人) 這一行, 頂部有 一塊白色區域, 可以當做是分割線
  2. 101(共2人) 這一行, 底部有一個很細的分割線, 差不多撐滿了一行
  3. 人物信息 這一行, 首次出現時, 只有底部有分割線, 而且還是 非撐滿一行的效果
  4. 人物信息 這一行, 最後出現時, 底部沒有分割線

不出意外, 很多同學的實現方式, 都是在 佈局的xml中, 用padding maring backgroud 實現.

但是遇到這種相同type類型的item, 只有中間有分割線, 最後一條沒有分割線的情況, 估計就有點棘手了.

莫慌, 看完本文, 你能輕鬆應對各種噁心的分割線.

具體實現

繼承 RecyclerView.ItemDecoration , 實現 核心的 getItemOffsets onDraw onDrawOver 方法, 通常 只需要實現 getItemOffsets onDraw 即可. 如果需要懸停效果, 才需要實現 onDrawOver

爲了讓封裝更輕量, 所以, 我並打算有任何實際操作方法.
只做核心業務的封裝, 具體實現 丟給調用者.

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        eachChildViewHolder(parent) { beforeViewHolder, viewHolder, afterViewHolder ->
            eachItemDoIt.invoke(canvas, parent, state, null, beforeViewHolder, viewHolder, afterViewHolder, false)
        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        eachChildViewHolder(parent, view) { beforeViewHolder, viewHolder, afterViewHolder ->
            eachItemDoIt.invoke(null, parent, state, outRect, beforeViewHolder, viewHolder, afterViewHolder, false)
        }
    }

核心方法

    fun eachChildViewHolder(
        parent: RecyclerView,
        targetView: View? = null,/*指定目標, 則只回調目標前後的ViewHolder*/
        callback: (
            beforeViewHolder: RecyclerView.ViewHolder?,
            viewHolder: RecyclerView.ViewHolder,
            afterViewHolder: RecyclerView.ViewHolder?
        ) -> Unit
    ) {

        val childCount = parent.childCount
        for (i in 0 until childCount) {
            val child = parent.getChildAt(i)
            val childViewHolder = parent.findContainingViewHolder(child)

            childViewHolder?.let {

                //前一個child
                var beforeViewHolder: RecyclerView.ViewHolder? = null
                //後一個child
                var afterViewHolder: RecyclerView.ViewHolder? = null

                if (i >= 1) {
                    beforeViewHolder = parent.findContainingViewHolder(parent.getChildAt(i - 1))
                }
                if (i < childCount - 1) {
                    afterViewHolder = parent.findContainingViewHolder(parent.getChildAt(i + 1))
                }

                if (targetView != null) {
                    if (targetView == child) {
                        callback.invoke(beforeViewHolder, it as RBaseViewHolder, afterViewHolder)
                        return
                    }
                } else {
                    callback.invoke(beforeViewHolder, it as RBaseViewHolder, afterViewHolder)
                }
            }
        }
    }

通過此方法, 到RecyclerView觸發onDraw時, 枚舉界面上的所有childView, 並且計算拿到 前一個後一個 childViewviewHolder 對象.(如果有)

這樣就可以通過 ViewHolder.getItemViewType 來區分, 各個item的類型, 也能夠通過,
前一個比較, 判斷是否是同類型的第一個位置
後一個比較, 判斷是否是同類型的最後一個位置

有個你想要的各種信息, 繪製分割線的事, 就交給 canvas 吧.

完整代碼

class DslItemDecoration : RecyclerView.ItemDecoration() {

    val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)
    val tempDrawRect = Rect()

    /**
     * 將3個方法, 合一調用. 通過參數, 來區分是那一個方法.
     *
     * outRect 不爲空時, 是 getItemOffsets 方法
     * canvas 不爲空時, 是 onDrawOver onDraw
     * isOverDraw 控制是否是 onDrawOver
     * */
    var eachItemDoIt: (
        canvas: Canvas?, parent: RecyclerView, state: RecyclerView.State, outRect: Rect?,
        beforeViewHolder: RecyclerView.ViewHolder?,
        viewHolder: RecyclerView.ViewHolder,
        afterViewHolder: RecyclerView.ViewHolder?,
        isOverDraw: Boolean
    ) -> Unit =
        { _, _, _, _, _, _, _, _ -> }

    override fun onDrawOver(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        eachChildRViewHolder(parent) { beforeViewHolder, viewHolder, afterViewHolder ->
            eachItemDoIt.invoke(canvas, parent, state, null, beforeViewHolder, viewHolder, afterViewHolder, true)
        }
    }

    override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
        eachChildRViewHolder(parent) { beforeViewHolder, viewHolder, afterViewHolder ->
            eachItemDoIt.invoke(canvas, parent, state, null, beforeViewHolder, viewHolder, afterViewHolder, false)
        }
    }

    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        eachChildRViewHolder(parent, view) { beforeViewHolder, viewHolder, afterViewHolder ->
            eachItemDoIt.invoke(null, parent, state, outRect, beforeViewHolder, viewHolder, afterViewHolder, false)
        }
    }

    fun eachChildViewHolder(
        parent: RecyclerView,
        targetView: View? = null,/*指定目標, 則只回調目標前後的ViewHolder*/
        callback: (
            beforeViewHolder: RecyclerView.ViewHolder?,
            viewHolder: RecyclerView.ViewHolder,
            afterViewHolder: RecyclerView.ViewHolder?
        ) -> Unit
    ) {

        val childCount = parent.childCount
        for (i in 0 until childCount) {
            val child = parent.getChildAt(i)
            val childViewHolder = parent.findContainingViewHolder(child)

            childViewHolder?.let {

                //前一個child
                var beforeViewHolder: RecyclerView.ViewHolder? = null
                //後一個child
                var afterViewHolder: RecyclerView.ViewHolder? = null

                if (i >= 1) {
                    beforeViewHolder = parent.findContainingViewHolder(parent.getChildAt(i - 1))
                }
                if (i < childCount - 1) {
                    afterViewHolder = parent.findContainingViewHolder(parent.getChildAt(i + 1))
                }

                if (targetView != null) {
                    if (targetView == child) {
                        callback.invoke(beforeViewHolder, it as RBaseViewHolder, afterViewHolder)
                        return
                    }
                } else {
                    callback.invoke(beforeViewHolder, it as RBaseViewHolder, afterViewHolder)
                }
            }
        }
    }
}

調用例子

public fun RecyclerView.dslItemDecoration(init: DslItemDecoration.() -> Unit) {
    addItemDecoration(DslItemDecoration().apply {
        init()
    })
}
dslItemDecoration {
   val line = getDimen(R.dimen.wt_line)
   eachItemDoIt = { canvas, _, _, outRect, _, viewHolder, afterViewHolder, _ ->
       if (viewHolder.itemViewType == R.layout.item_info_group_head) {
           outRect?.set(0, 10 * dpi, 0, line)

           tempDrawRect.set(
               0,
               viewHolder.itemView.top - 10 * dpi,
               viewHolder.itemView.right,
               viewHolder.itemView.top
           )
           paint.color = getColor(R.color.wt_dark_bg)
           canvas?.drawRect(tempDrawRect, paint)

           tempDrawRect.set(
               0,
               viewHolder.itemView.top - line,
               viewHolder.itemView.right,
               viewHolder.itemView.top
           )
           paint.color = getColor(R.color.wt_line)
           canvas?.drawRect(tempDrawRect, paint)

           tempDrawRect.set(
               0,
               viewHolder.itemView.bottom,
               viewHolder.itemView.right,
               viewHolder.itemView.bottom - line
           )
           canvas?.drawRect(tempDrawRect, paint)

       } else if (viewHolder.itemViewType == R.layout.item_info_single_text) {
           if (afterViewHolder?.itemViewType == R.layout.item_info_single_text) {
               outRect?.set(0, 0, 0, line)

               paint.color = getColor(R.color.wt_line)
               tempDrawRect.set(
                   25 * dpi,
                   viewHolder.itemView.bottom,
                   viewHolder.itemView.right,
                   viewHolder.itemView.bottom - line
               )

               canvas?.drawRect(tempDrawRect, paint)
           }
       }
   }
}

結束…


羣內有各(pian)種(ni)各(jin)樣(qun)的大佬,等你來撩.

聯繫作者

點此快速加羣

請使用QQ掃碼加羣, 小夥伴們都在等着你哦!

關注我的公衆號, 每天都能一起玩耍哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章