简介
- Decoration在英文中的意思是装饰,在RecyclerView中ItemDecoration就是每一个Item的装饰器。
- 在上古时期,ListView很是流行,当时我们如果想要设置一个分隔线,我们仅仅在xml中指定 android:divider="1dp",就可以了,但是RecyclerView中没有提供相应的属性设置。
- 如果想要实现分割线的效果,我们可以借助于ItemDecoration。
使用
实现分割线效果
-
效果如下
rv_decoration.gif
实现过程
- 接上篇文章的代码做如下修改 :上篇文章
- 修改activity_main.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="match_parent"
//新增了一个背景色
android:background="#f4f4f4"
tools:context=".GridLayoutManagerActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 修改item.xml文件的bg
<?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="#ffffff"
android:padding="16dp">
<ImageView
android:id="@+id/ivCover"
android:layout_width="80dp"
android:layout_height="80dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="4dp"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#333333"
android:textSize="18sp"
app:layout_constraintLeft_toRightOf="@+id/ivCover"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/ivCover" />
<TextView
android:id="@+id/tvDesc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginTop="8dp"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@+id/ivCover"
app:layout_constraintLeft_toRightOf="@+id/ivCover"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvName"
tools:text="测试测试测试测试" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 新增ComItemDecoration文件
class ComItemDecorate : RecyclerView.ItemDecoration() {
/**
* view:指代着每一个Item
* outRect 是一个为0的Rect,填充在Item的四周
*/
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
// 让item的rect的底部 高度设置为16f
outRect.bottom = DensityUtils.dip2pxInt(parent.context, 16f)
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
}
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
}
}
- 为RecyclerView设置ItemDecoration
var adapter = GridLayoutAdapter(items)
rv.addItemDecoration(ComItemDecorate())
rv.adapter = adapter
总结
- 在上述代码中,我们让ItemView四周的rect的底部高度设置为2,这样就露出了背景色,形成了分割线,
- 这样的分隔线的线程取决于RecyclerView的背景色。
- 对此,在RecyclerView.ItemDecoration中为我们提供了onDraw()方法,可通过该方法画出一个分割线
ItemDecoration.onDraw()
- onDraw()方法是具有绘制的能力
- 该方法是和getItemOffsets()配合使用的,getItemOffsets()是把ItemView的四周撑开,而我们的onDraw()方法,通过计算每个Item所处的坐标点,来绘制图形,从而形成我们想要的效果
- 我们要在每个Item的底部画一个高度16的红色矩形,填充在每一个Item的底部
- 代码如下--- 使用ItemDecoration()中的onDraw()方法
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
// item数
val childItemCount = parent.childCount
for (index in 0..childItemCount-1) {
val itemView = parent.getChildAt(index)
val top = itemView.top
val left = parent.paddingLeft
val right = parent.paddingRight
val bottom = itemView.top + DensityUtils.dip2pxInt(parent.context, 16f)
val rect = Rect(top, left, right, bottom)
c.drawRect(rect, drawPaint)
}
}
-
效果如下
rv_draw.gif
getItemOffset()配合onDraw()方法实现时间轴
-
效果图如下
rv_timeline.gif - 实现代码如下
class TimelineItemDecoration : RecyclerView.ItemDecoration() {
private val paint: Paint = Paint()
private val offset = 120
init {
//设置画笔
paint.isAntiAlias = true
paint.style = Paint.Style.FILL_AND_STROKE
paint.color = Color.RED
}
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
val position = parent.getChildAdapterPosition(view)
//当是第一条数据的时候,不设置outRect的顶部
val topOffset = offset
if (position != 0) {
outRect.top = topOffset
}
outRect.left = offset
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// 条目个数
val childItemCount = parent.childCount
for (position in 0 until childItemCount) {
// 对应的ItemView
val itemView = parent.getChildAt(position)
val index = parent.getChildAdapterPosition(itemView)
// 第一个条目 没有做outRect的top拉伸
var dividerTop = itemView.top - offset
if (index == 0) {
dividerTop = itemView.top
}
val dividerBottom = itemView.bottom
val dividerLeft = parent.paddingLeft
val dividerRight = parent.width - parent.paddingRight
//画中心圆
val radiusX = dividerLeft + offset / 2
val radiusY = (dividerBottom - dividerTop) / 2 + dividerTop
c.drawCircle(radiusX.toFloat(), radiusY.toFloat(), radiusX / 2f, paint)
// 画第一条直线
val oneLineStartX = dividerLeft + radiusX
val oneLineStartY = dividerTop
val oneLineEndX = dividerLeft + radiusX
val oneLineEndY = dividerTop + (dividerBottom - dividerTop) / 2 - radiusX / 2
c.drawLine(
oneLineStartX.toFloat(), oneLineStartY.toFloat(),
oneLineEndX.toFloat(), oneLineEndY.toFloat(), paint
)
// 画第二条直线
val twoLineStartX = dividerLeft + radiusX
val twoLineStartY = (dividerBottom - dividerTop) / 2 + dividerTop
val twoLineEndX = dividerLeft + radiusX
val twoLineEndY = dividerBottom
c.drawLine(
twoLineStartX.toFloat(),
twoLineStartY.toFloat(), twoLineEndX.toFloat(), twoLineEndY.toFloat(), paint
)
}
}
}
onDrawOver()
- 此方法的用处就是将绘制的内容绘制在ItemView之上
- 相比较于 onDraw(),onDraw()是将内容绘制在ItemView之下的。
网友评论