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)
-