美文网首页
RecyclerView通讯录粘性头部/吸顶效果(ItemDec

RecyclerView通讯录粘性头部/吸顶效果(ItemDec

作者: 小强开学前 | 来源:发表于2020-04-03 15:04 被阅读0次

Well Well


最终效果

Github源代码

注意点:

  1. parent.childCount为当前可见区域内的所有child的个数
  2. TextPaint绘制text时x坐标是起始x坐标,但y是baseLine,也就是英语本子上第三根线的位置。
  3. view.left、top、right、bottom是view相对于父布局的位置,left、right是view相对于父布局最左边的距离,top、bottom是相对于父布局最上边的距离。

1. Decoration可以对每个显示在屏幕上的item进行控制

class PinnedStickerDecoration :RecyclerView.ItemDecoration(){
    override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {
        super.getItemOffsets(outRect, view, parent, state)
        outRect.bottom = 1
    }
}

getItemOffset方法相当于给item在layout后的位置上再添加偏移量,它不是改变padding,而是类似marginOffset。如下图

item的父布局如下,背景色为白色,recyclerview背景色为灰色。

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:paddingVertical="8dp"
    android:layout_marginTop="5dp">
 override fun getItemOffsets(outRect: Rect,view: View,parent: RecyclerView,state: RecyclerView.State) {
                super.getItemOffsets(outRect, view, parent, state)
                val pos = parent.getChildAdapterPosition(view)
                // 每组第一个留出24dp距离
                if (pos == 0 || tests[pos][0].toUpperCase() != tests[pos - 1][0].toUpperCase()) {
                    outRect.top = (dp1 * 24).toInt()
                } else {  // 其余的1dp
                    outRect.top = dp1.toInt()
                }
            }

效果如下:


offset效果

24dp可能看不出,1dp变宽还是很明显的。
padding只会影响内部子控件,margin才会影响自身位置以及兄弟布局的位置。

通讯录分组不带粘性头部/吸顶效果的完成

参考的小武站台的文章。
避免过度绘制,recyclerview及其所有父布局背景去除,直接在onDraw中绘制背景色。

  1. 偏移设置(每组第一个偏移24dp,该组其余item偏移1dp)
  2. onDraw()方法绘制文字及分割线(每组第一个绘制24dp高的灰色区域并绘制文字,该组其余item顶部绘制1dp高度的灰色区域)

Item完整xml:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"
    android:paddingVertical="8dp"
    tools:background="@android:color/white">

    <ImageView
        android:id="@+id/img_item_file"
        android:layout_width="64dp"
        android:layout_height="64dp"
        android:layout_marginStart="8dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:src="@color/colorPrimary" />

    <TextView
        android:id="@+id/name_item_file"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@id/desc_item_file"
        app:layout_constraintStart_toEndOf="@+id/img_item_file"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed"
        tools:text="Life" />

    <TextView
        android:id="@+id/desc_item_file"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="24dp"
        android:textColor="@color/font_gray_color"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toEndOf="@+id/img_item_file"
        app:layout_constraintTop_toBottomOf="@+id/name_item_file"
        tools:text="Life" />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="8dp"
        android:src="@drawable/ic_chevron_right"
        android:tint="@color/font_gray_color"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

recyclerView设置ItemDecoration:

recycler_view_main.addItemDecoration(object : RecyclerView.ItemDecoration() {
            val textPaint = TextPaint()
            val bgdPaint = Paint()

            init {
                textPaint.apply {
                    isAntiAlias = true
                    color = ContextCompat.getColor(this@MainActivity, android.R.color.darker_gray)
                    typeface = Typeface.DEFAULT_BOLD
                    textSize = dp1 * 12
                }
                bgdPaint.color = ContextCompat.getColor(this@MainActivity, R.color.divide_color)
            }

            override fun getItemOffsets(
                outRect: Rect,
                view: View,
                parent: RecyclerView,
                state: RecyclerView.State
            ) {
                super.getItemOffsets(outRect, view, parent, state)
                val pos = parent.getChildAdapterPosition(view)
                if (pos == 0 || tests[pos][0].toUpperCase() != tests[pos - 1][0].toUpperCase()) {
                    outRect.top = (dp1 * 24).toInt()
                } else {
                    outRect.top = dp1.toInt()
                }
            }

            override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
                super.onDraw(c, parent, state)
                for (i in 0 until parent.childCount) {
                    val view = parent[i]
                    val pos = parent.getChildAdapterPosition(view)
                    if (pos == 0 || tests[pos][0].toUpperCase() != tests[pos - 1][0].toUpperCase()) {
                        c.drawRect(
                            view.left.toFloat(), view.top - dp1 * 24, view.right.toFloat(),
                            view.top.toFloat(), bgdPaint
                        )
                        c.drawText(
                            tests[pos][0].toString().toUpperCase(Locale.ENGLISH),
                            view.left.toFloat() + dp1 * 16,
                            view.top.toFloat() - dp1 * 6,
                            textPaint
                        )
                    } else {
                        c.drawRect(
                            view.left.toFloat(), view.top - dp1, view.right.toFloat(),
                            view.top.toFloat(), bgdPaint
                        )
                    }
                }
            }
}

效果:


Gif_20200403_142511.gif

通讯录带粘性头部/吸顶效果

效果如开始所见。可以完全用onDrawOver()实现。

    override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
                super.onDrawOver(c, parent, state)
                for (i:Int in 0 until parent.childCount) {
                    val view = parent[i]
                    val pos = parent.getChildAdapterPosition(view)
                    if (view.top > dp1*24) {
                        // 第一个
                        if (pos == 0 || tests[pos][0].toUpperCase() != tests[pos - 1][0].toUpperCase()) {
                            // 顶部显示
                            c.drawRect(view.left.toFloat(),view.top - dp1 * 24,view.right.toFloat(),view.top.toFloat(),bgdPaint)
                            c.drawText(=tests[pos][0].toString().toUpperCase(Locale.ENGLISH),view.left.toFloat() + dp1 * 16,view.top - dp1 * 6,textPaint)
                        } else {
                            // 分隔线
                            c.drawRect(view.left.toFloat(),view.top - dp1,view.right.toFloat(),view.top.toFloat(),bgdPaint)
                        }
                    } else {
                        // 最后一个
                        if (pos + 1 < tests.size && tests[pos][0].toString().toUpperCase(Locale.ENGLISH) != tests[pos + 1][0].toString().toUpperCase(
                                Locale.ENGLISH)) {

                            // 拖动
                            val stickY: Float = min(view.bottom.toFloat(), dp1 * 24)
                            c.drawRect(view.left.toFloat(), stickY - dp1 * 24,view.right.toFloat(),stickY,bgdPaint)
                            c.drawText(tests[pos][0].toString().toUpperCase(Locale.ENGLISH),view.left.toFloat() + dp1 * 16,stickY - dp1 * 6,textPaint)
                        } else {
                            // 置顶
                            c.drawRect(view.left.toFloat(),0f,view.right.toFloat(),dp1 * 24,bgdPaint)
                            c.drawText(tests[pos][0].toString().toUpperCase(Locale.ENGLISH), view.left.toFloat() + dp1 * 16,dp1 * 18,textPaint)
                        }
                    }
                }
            }

以上代码是优化后的思路。
整体思路:


整体思路

优化后的思路:


最后思路

相关文章

网友评论

      本文标题:RecyclerView通讯录粘性头部/吸顶效果(ItemDec

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