DividerItemDecoration为RecyclerView自带分隔线
recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.HORIZONTAL))
这个分隔线的样子是系统自带的,也可以自定义分隔线的样子,通过DividerItemDecoration的setDrawable来设置分隔线的样子,比如,我们希望两行之前的item间隔10dp,分隔线为完全透明,则可以定义一个高为10dp的无颜色drawable对象传给DividerItemDecoration即可,如下:
val dividerItemDecoration = DividerItemDecoration(baseActivity, DividerItemDecoration.VERTICAL)
dividerItemDecoration.setDrawable(GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE // 设置shape为矩形
setSize(10, dp(10)) // 宽设置多少无所谓,高设置为10dp
})
recyclerView.addItemDecoration(dividerItemDecoration)
通过ItemTouchHelper可实现item的drag操作或swipe操作
val itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback(myAdapter))
itemTouchHelper.attachToRecyclerView(recyclerView)
实例参考的是官方Demo:Support v7 Demo > RecyclerView > ItemTouchHelper,这里面包含移动示例和左右滑动示例:Drag and Drop Activity、Swipe To Dismiss。
长按Item,然后即可进行拖动,实现效果如下:
代码也非常的简单,如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val recyclerView = RecyclerView(this)
setContentView(recyclerView)
val myAdapter = MyAdapter()
recyclerView.layoutManager = GridLayoutManager(this, 3)
recyclerView.adapter = myAdapter
val itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback(myAdapter))
itemTouchHelper.attachToRecyclerView(recyclerView)
}
class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
val dataList = mutableListOf("1", "2", "3", "4", "5", "6", "7", "8", "9")
private val random = Random(System.currentTimeMillis())
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val textView = LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_1, parent, false) as TextView
textView.setBackgroundColor(randomColor())
return MyViewHolder(textView)
}
private fun randomColor(): Int {
val red = random.nextInt(256)
val green = random.nextInt(256)
val blue = random.nextInt(256)
return Color.rgb(red, green, blue)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.textView.text = dataList[position]
}
override fun getItemCount() = dataList.size
}
class MyViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
class ItemTouchHelperCallback(private val adapter: MyAdapter) : ItemTouchHelper.Callback() {
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
// 设置上、下、左、右四个方向皆可进行drag操作
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.START or ItemTouchHelper.END
// 设置禁止swipe操作,0表示禁用,可使用ItemTouchHelper的UP、DOWN、START、END来控制允许哪个方向的swipe操作
val swipeFlags = 0
return makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val from = viewHolder.adapterPosition
val to = target.adapterPosition
Log.i("ABCD", "移动 $from 到 $to")
val prevData = adapter.dataList.removeAt(from)
adapter.dataList.add(if (to > from) to - 1 else to, prevData)
adapter.notifyItemMoved(from, to)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { }
}
}
后续 (2021-07-16):
今天发现onMove中关于数据集合的操作逻辑是有问题的,这是抄的官方例子的代码,官方例子就是有问题的,在完成移动后,如果我们调用一下adapter.notifyDataSetChanged(),就会发现顺序变了,下面,举个简单的例子来说明问题:
首先,要理解两个函数的功能:
1、removeAt(index),移除指定位置的元素,这个比较好理解。
2、add(index, e),在指定的位置插入元素,指定位置之前的元素会依次往后移动,举例如下:
由0、1、2、3、4的元素组成的列表,分别在位置1、5、6插入一个元素5,如下:
// 在位置1插入元素
val list = mutableListOf(0, 1, 2, 3, 4)
list.add(1, 5)
println(list) // 输出:[0, 5, 1, 2, 3, 4]
// 在位置5插入元素
val list = mutableListOf(0, 1, 2, 3, 4)
list.add(5, 5)
println(list) // [0, 1, 2, 3, 4, 5]
// 在位置6插入元素
val list = mutableListOf(0, 1, 2, 3, 4)
list.add(6, 5) // 抛异常:IndexOutOfBoundsException
println(list)
可以看到,原列表有5个元素,最大索引为4,可以在索引为5的位置插入元素,但是不能在索引为6的位置插入元素。
Ok,了解了这两个函数的功能之后,下面就来说前面示例的Bug:
把元素2移动到元素3的位置:
fun main() {
val list = mutableListOf(0, 1, 2, 3, 4)
val from = 2
val to = 3
val number = list.removeAt(from)
list.add(if (to > from) to - 1 else to, number)
println(list) // 输出:[0, 1, 2, 3, 4]
}
可以发现列表元素的排序没有发生变化,这是因为to > from,所以执行了to - 1操作,to - 1 = 2,当list.removeAt(2)之后,列表为:[0, 1, 3, 4],元素2没有了,此时又把2插入到位置2,就相当于又插入到了之前的位置。所以解决方案就是不要减1,正确答案如下:
list.add(to, number)