Well Well
最终效果
注意点:
- parent.childCount为当前可见区域内的所有child的个数
TextPaint
绘制text时x坐标是起始x坐标,但y是baseLine,也就是英语本子上第三根线的位置。- 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中绘制背景色。
- 偏移设置(每组第一个偏移24dp,该组其余item偏移1dp)
- 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)
}
}
}
}
以上代码是优化后的思路。
整体思路:
整体思路
优化后的思路:
最后思路
网友评论