Android development adds index ceiling effect

Android development adds index ceiling effect

import android.graphics.Canvas
import android. graphics. Paint
import android.graphics.Rect
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.purui.mobile.R
import com.purui.mobile.base.dip2sp
import com.purui.mobile.utils.ResUtils

abstract class StickyItemDecoration: RecyclerView.ItemDecoration() {<!-- -->

    companion object {<!-- -->
        // margin
        private val indexPaddingHorizontal = ResUtils.getDimenF(R.dimen.letter_padding_start)
        // index text color
        private val indexFontColor = ResUtils.getColor(R.color.font_color_dark)
        // index bar background color
        private val indexBgColor = ResUtils.getColor(R.color.divider_color)
        // index bar height
        private val indexHeight = ResUtils.getDimen(R.dimen.letter_height)
        // font size
        private val indexTextSize = dip2sp(16f)
    }

    private val mIndexBgPaint by lazy {<!-- --> Paint() }

    private val mTextPaint by lazy {<!-- --> Paint() }

    init {<!-- -->
        mTextPaint.let {<!-- -->
            it.textSize = indexTextSize
            it.isAntiAlias = true
            it.color = indexFontColor
        }

        mIndexBgPaint.let {<!-- -->
            it.isAntiAlias = true
            it.color = indexBgColor
        }
    }

    /**
     * recyclerView draws onDraw -> item.onDraw -> onDrawOver
     */
    override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {<!-- -->
        for (index in 0 until parent. childCount) {<!-- -->
            val childView = parent. getChildAt(index)
            childView?.let {<!-- -->childViewNotNull->
                // draw each index bar
                val indexRect = Rect()
                val position = parent. getChildAdapterPosition(childViewNotNull)
                if (isIndexItem(position) & amp; & amp; position >= 0) {<!-- -->
                    // Control the drawing position of the index bar
                    indexRect.apply {<!-- -->
                        top = childViewNotNull. top - indexHeight
                        bottom = childViewNotNull.top
                        left = parent. paddingLeft
                        right = parent.width - parent.paddingRight
                    }
                    // Draw the background of the index bar
                    c. drawRect(indexRect, mIndexBgPaint)
                    // Draw index bar text
                    c.drawText(getIndexTitle(position),
                        indexPaddingHorizontal,
                        getBaseLineY(paint = mTextPaint, centerY = indexRect. centerY()),
                        mTextPaint)
                }
            }
        }
    }

    private fun getBaseLineY(paint:Paint,centerY:Int):Float {<!-- -->
        return centerY - ( paint.fontMetricsInt.bottom + paint.fontMetricsInt.top ) / 2f
    }

    /**
     * recyclerView draws onDraw -> item.onDraw -> onDrawOver
     * Draw the index bar overlaying the item
     */
    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {<!-- -->
        val firstView = parent. getChildAt(0)
        var nextView:View? = null;
        if (1 < parent. childCount) {<!-- -->
            nextView = parent.getChildAt(1) // next item
        }
        firstView?.let {<!-- --> firstViewNotNull->
            val floatIndexRect = Rect()
            val position = parent. getChildAdapterPosition(firstViewNotNull)
            val firstLetterWillMeetSecondLetter = firstViewNotNull.bottom - indexHeight < parent.paddingTop
            val secondItemViewIsTimeItem = if (nextView == null) false else isIndexItem(parent. getChildAdapterPosition(nextView))
            val currentTitleNotEqualsToNextTitle = if (nextView == null) false else !isSameIndexTitle(parent.getChildAdapterPosition(nextView),position)
            // If the next view is not empty, the first index is about to touch the next index, the next item needs to display the index, and the index text of the next item is different from the current item
            if (firstLetterWillMeetSecondLetter
                 & amp; & amp; secondItemViewIsTimeItem
                 & amp; & amp; currentTitleNotEqualsToNextTitle) {<!-- -->
                // push up following the last item
                floatIndexRect.top = firstViewNotNull.bottom - indexHeight // The bottom of the first item is the bottom of the index of the hover prompt (the displayed index is pushed out of the list)
                floatIndexRect.bottom = firstViewNotNull.bottom
            } else {<!-- -->
                // Fixed floating at the top
                floatIndexRect.top = parent.paddingTop
                floatIndexRect.bottom = floatIndexRect.top + indexHeight
            }
            floatIndexRect.left = parent.paddingLeft
            floatIndexRect.right = parent.width - parent.paddingRight

            // Draw the background of the index bar
            c.drawRect(floatIndexRect, mIndexBgPaint)
            // Draw index bar text
            c.drawText(getIndexTitle(position),
                       indexPaddingHorizontal,
                       getBaseLineY(paint = mTextPaint, centerY = floatIndexRect. centerY()),
                       mTextPaint)
        }
    }

    /**
     * Used to set the offset around the item, similar to setting padding and margin effects,
     * The empty effect is used to draw the dividing line
     */
    override fun getItemOffsets(
        outRect: Rect,
        view: View,
        parent: RecyclerView,
        state: RecyclerView. State
    ) {<!-- -->
        val position: Int = parent. getChildAdapterPosition(view)
        if (position >=0 & amp; & amp; isIndexItem(position)) {<!-- -->
            outRect.top = indexHeight
        } else{<!-- -->
            outRect.top = 0
        }
    }

    /**
     * External decision whether to index item
     */
    abstract fun isIndexItem(position: Int): Boolean

    /**
     * index title
     */
    abstract fun getIndexTitle(position: Int):String

    /**
     * same index title
     */
    abstract fun isSameIndexTitle(position1: Int, position2: Int): Boolean
}

Use this to control recyclerview

recyclerview.addItemDecoration(object : StickyItemDecoration() {<!-- -->
         override fun isIndexItem(position: Int) = if (list.size <= 0 || position < 0) false else list[position].isHeader

        override fun getIndexTitle(position: Int) = if (list.size <= 0 || position < 0) "" else list[position].showHeader?:""

        override fun isSameIndexTitle(position1: Int, position2: Int) = if (list.size <= 0 || position1 < 0 || position2 < 0) true else list[position1].showHeader == list[position2].showHeader
    })