您当前的位置:首页 > 计算机 > 编程开发 > 安卓(android)开发

android 开发RecyclerView

时间:02-06来源:作者:点击数:

简单的RecyclerView分隔线

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)

实现Item拖动

通过ItemTouchHelper可实现item的drag操作或swipe操作

val itemTouchHelper = ItemTouchHelper(ItemTouchHelperCallback(myAdapter))
itemTouchHelper.attachToRecyclerView(recyclerView)

实现Grid item移动的示例

实例参考的是官方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)
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门