logo头像

我有一个梦想

RecyclerView筛选框圆角实现

本文于 704 天之前发表,文中内容可能已经过时。

筛选框是app的常用功能,如何实现下图红圈功能呢?

image-recyclerView
首先左侧目录可以通过一个RecyclerView实现,右侧同样可以实现一个RecyclerView,左侧的RecyclerView中我们想实现圆角的,但是该圆角的View已经不在ViewHolder中,我们很难在ViewHolder中画出两个圆角,所以我们通过其他方法实现

ItemDecoration有三个重要方法:

onDraw:在ViewHolder下方绘制

onDrawOver:在ViewHolder上方绘制

getItemOffsets:ViewHolder的偏移量

其中我们可以通过onDrawOver实现,将两个圆角绘制在ViewHolder上方显示出来

首先,我们搭建一个RecyclerView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//MainActivity
private val mRecyclerView by lazy {
findViewById<RecyclerView>(R.id.recycler)
}

private var mAdapter: MainAdapter? = null
private var mList: ArrayList<Item> = ArrayList()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initData()
mAdapter = MainAdapter(mList, object : MainAdapter.OnItemClick {
override fun onItemClick(view: View, position: Int) {
for (i in mList) {
i.apply {
isChecked = false
}
}
mList[position].apply {
isChecked = true
}
mAdapter?.notifyDataSetChanged()
}
})
mRecyclerView.adapter = mAdapter
mRecyclerView.layoutManager = LinearLayoutManager(this)
}

private fun initData() {
mList.clear()
for (i in 0..10) {
mList.add(Item("第${i}个"))
}
}

data class Item(val text: String, var isChecked: Boolean = false)

class MainAdapter(private val list: ArrayList<Item>, private val onItemClick: OnItemClick? = null) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var context: Context? = null

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.vh_item, parent, false)
context = parent.context
return MainViewHolder(view, onItemClick)
}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = list[position]
holder as MainViewHolder
holder.setText(item.text)
(if (item.isChecked) context?.let { getColor(it, R.color.white) } else Color.parseColor("#F6F6F6"))?.let { holder.setBackgroundColor(it) }
holder.itemView.tag = if (item.isChecked) "true" else "false"
}

override fun getItemCount(): Int = list.size

class MainViewHolder(itemView: View, onItemClick: OnItemClick?) : RecyclerView.ViewHolder(itemView) {
init {
itemView.setOnClickListener {
onItemClick?.onItemClick(it, adapterPosition)
}
}

fun setText(text: String?) {
itemView.findViewById<TextView>(R.id.item_text).text = text ?: ""
}

fun setBackgroundColor(color: Int) {
itemView.setBackgroundColor(color)
}
}

interface OnItemClick {
fun onItemClick(view: View, position: Int)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//activity_main
<?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"
tools:context=".MainActivity"
>


<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#F6F6F6"
/>

</androidx.constraintlayout.widget.ConstraintLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//vh_item
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#F6F6F6"
>

<TextView
android:id="@+id/item_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="123"
android:textSize="16sp"
android:textColor="@color/black"
android:gravity="center"/>

</androidx.constraintlayout.widget.ConstraintLayout>

当在选中的时候改变VH的颜色,之后我们需要添加一个ItemDecoration,在ItemDecoration中我们需要知道选中的View,获取View宽高后,进行上下圆角的绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class BorderItemDecoration() : RecyclerView.ItemDecoration() {

override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDrawOver(c, parent, state)
for (view in parent.children) {
if (view.tag == "true") {
drawTop(c, view)
drawBottom(c, view)
}
}
}

private fun drawTop(c: Canvas, view: View) {
val paint = Paint()
val rect = Rect()
val top = view.top
val radius = view.resources.displayMetrics.density * 40
rect.set((view.width - radius).toInt(), (top - radius).toInt(), view.width, view.top)
paint.color = Color.WHITE
paint.style = Paint.Style.FILL
c.drawRect(rect, paint)

paint.color = Color.parseColor("#F6F6F6")
c.drawCircle(view.width - radius, top - radius, radius, paint)
}

private fun drawBottom(c: Canvas, view: View) {
val paint = Paint()
val rect = Rect()
val bottom = view.bottom
val radius = view.resources.displayMetrics.density * 40
c.translate(0f, 0f)
rect.set((view.width - radius).toInt(), (bottom + radius).toInt(), view.width, view.bottom)
paint.color = Color.WHITE
paint.style = Paint.Style.FILL
c.drawRect(rect, paint)

paint.color = Color.parseColor("#F6F6F6")
c.drawCircle(view.width - radius, bottom + radius, radius, paint)
}
}

绘制的细节如下:

  1. 先绘制获取选中view的坐标,计算出圆角半径的矩形位置,使用白色填充
  2. 在圆角半径内画圆,填充为#F6F6F6颜色,剩下的就是圆角白色

上下方法相同。

效果如下:

video_recyclerView