热点新闻
Android RecycleView 实现拖拽和侧滑删除效果
2023-07-13 07:33  浏览:570  搜索引擎搜索“爱农网”
温馨提示:信息一旦丢失不一定找得到,请务必收藏信息以备急用!本站所有信息均是注册会员发布如遇到侵权请联系文章中的联系方式或客服删除!
联系我时,请说明是在爱农网看到的信息,谢谢。
展会发布 发布信息 广告合作 软文发布



效果.gif

简介

项目需要做一个拖拽排序的需求(类似头条栏目排序),原先随意找了个三方库简单的处理了一下.但是随着项目的的迭代,越来越多的需求堆积下来,三方库不满足自己定制的一些需求.所以决定自己写一写这个效果

思路:

RecycleView 实现列表样式,ItemTouchHelper实现子条目的拖拽和侧滑删除.中间牵扯到指定条目禁止排序,禁止删除的功能.

实现

1.页面搭建

1.1 主页面Activity代码

package com.wkq.dragrecycle import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.Toast import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.wkq.dragrecycle.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { var stringList = arrayListOf<String>("北京", "河北", "河南", "山东", "天津", "陕西", "山西", "石家庄", "内蒙古", "黑龙江", "吉林", "新疆", "西藏", "安徽", "湖北" ) private lateinit var mGridItemDecoration: GridSpaceItemDecoration var binding: ActivityMainBinding? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) initView() } private fun initView() { //设置布局选择 binding!!.btChange.setonClickListener { when ( binding!!.rvContent.layoutManager) { is GridLayoutManager -> { binding!!.rvContent.layoutManager = LinearLayoutManager(this) } else -> { binding!!.rvContent.layoutManager = GridLayoutManager(this, 4) } } } //默认网格布局 var mAdapter = DragAdapter(this) mGridItemDecoration = GridSpaceItemDecoration(4) binding!!.rvContent.addItemDecoration(mGridItemDecoration, 0) binding!!.rvContent.layoutManager = GridLayoutManager(this, 4) binding!!.rvContent.adapter = mAdapter mAdapter.addItems(stringList) //拖拽绑定的监听器 var callBack = DragItemCallback(mAdapter,true) //拖拽触摸的帮助类 var itemTouchHelper = ItemTouchHelper(callBack) //绑定Rv itemTouchHelper.attachToRecyclerView(binding!!.rvContent) mAdapter.setonItemClickListener(object : DragAdapter.onItemClickListener { override fun onItemClick(position: Int) { Toast.makeText(this@MainActivity, mAdapter.getItems().get(position), Toast.LENGTH_SHORT).show() } override fun onItemLongClick(holder: DragAdapterViewHolder) { //长按开启拖拽 itemTouchHelper.startDrag(holder) } }) } }

1.2 Adapter 代码实现(注意默认禁止操作Item的位置默认0)

package com.wkq.dragrecycle import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.RecyclerView import com.wkq.dragrecycle.databinding.ItemDragBinding class DragAdapter(mContext: Context,limitPosition:Int=0) : RecyclerView.Adapter<DragAdapterViewHolder>() { //上下文 var mContext=mContext // 固定条目的位置 val limitPosition = limitPosition //内容数据 private var contentList = ArrayList<String>() override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DragAdapterViewHolder { var binding = DataBindingUtil.inflate<ItemDragBinding>( LayoutInflater.from(mContext), R.layout.item_drag, parent, false ) var holder = DragAdapterViewHolder(binding.root) holder.setDataBinding(binding) return holder } override fun onBindViewHolder(holder: DragAdapterViewHolder, position: Int) { var binding = holder.getBinding() as ItemDragBinding if (limitPosition==position){ binding.tvContent.setBackgroundResource(R.drawable.shape_radius5_green2) }else{ binding.tvContent.setBackgroundResource(R.drawable.shape_radius5_green) } binding.tvContent.text = contentList.get(position) binding.tvContent.setonClickListener { mListener?.onItemClick(holder.adapterPosition) } binding.tvContent.setonLongClickListener { mListener?.onItemLongClick(holder) return@setonLongClickListener true } } override fun getItemCount(): Int { return contentList.size } fun addItems(items: ArrayList<String>) { contentList.addAll(items) } fun addItems(item: String) { contentList.add(item) } fun remove(item: String) { contentList.remove(item) notifyDataSetChanged() } fun getItems():ArrayList<String>{ return contentList } private var mListener: OnItemClickListener? = null fun setonItemClickListener(listener: OnItemClickListener) { mListener = listener } interface onItemClickListener { fun onItemClick(position: Int) fun onItemLongClick(holder: DragAdapterViewHolder) } }

2. DragItemCallback 拖拽的接口回调(主要实现逻辑)

package com.wkq.dragrecycle import android.graphics.drawable.GradientDrawable import android.util.Log import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import java.util.* class DragItemCallback(adapter: DragAdapter, isSwipe: Boolean = true) : ItemTouchHelper.Callback() { private var mAdapter = adapter private var mData = adapter.getItems() private var isSwipe = isSwipe // 这个方法用于让RecyclerView拦截向上滑动,向下滑动,想左滑动 // makeMovementFlags(dragFlags, swipeFlags); dragFlags显示项目支持拖动的方向 swipe标记物品可以滑动的方向 0 表示禁止 // 网格布局:上下左右 // 线性布局:上下/左右 override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { //拖动标记 0 表示禁止 var dragFlags = 0 //侧滑标记 var swipeFlags = 0 var layoytManager = recyclerView.layoutManager if (layoytManager is GridLayoutManager) { //网格布局 默认禁止侧滑 if (viewHolder.adapterPosition != mAdapter.limitPosition) { dragFlags =ItemTouchHelper.LEFT or ItemTouchHelper.UP or ItemTouchHelper.RIGHT or ItemTouchHelper.DOWN } return makeMovementFlags(dragFlags, swipeFlags) } else if (layoytManager is LinearLayoutManager) { //处理 禁止滑动模块得逻辑 禁止移动得模块 禁止左右滑动 if (viewHolder.adapterPosition != mAdapter.limitPosition) { dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END } return makeMovementFlags(dragFlags, swipeFlags) } //默认 禁止滑动 禁止拖拽 return makeMovementFlags(dragFlags, swipeFlags) } override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { // 起始位置 val fromPosition = viewHolder.adapterPosition // 结束位置 val toPosition = target.adapterPosition // 固定位置 处理笃定位置不要移动 if (fromPosition == mAdapter.limitPosition || toPosition == mAdapter.limitPosition) { return false } // 根据滑动方向 交换数据 for 循环不包含 toPosition 1 替代2 2替代3 3 替代4 if (fromPosition < toPosition) { // 含头不含尾 for (index in fromPosition until toPosition) { //交换集合得位置 Collections.swap(mData, index, index + 1) } } else { // 含头不含尾 for (index in fromPosition downTo toPosition + 1) { Collections.swap(mData, index, index - 1) } } // 刷新布局 mAdapter.notifyItemMoved(fromPosition, toPosition) return true } override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { // direction 滑动的状态 //ItemTouchHelper.START表示向左滑动 // ItemTouchHelper.END 向右边滑动 val position = viewHolder.adapterPosition if (position != mAdapter.limitPosition) { //表示 禁止移动得布局 mData.removeAt(position) mAdapter.notifyItemRemoved(position) } } override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { if (viewHolder == null) return //空闲状态 if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { ViewCompat.animate(viewHolder!!.itemView).setDuration(100).scaleX(1.2F).scaleY(1.2F) .start() } super.onSelectedChanged(viewHolder, actionState) } override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { // 恢复显示 // 这里不能用if判断,因为GridLayoutManager是LinearLayoutManager的子类,改用when,类型推导有区别 ViewCompat.animate(viewHolder.itemView).setDuration(100).scaleX(1F).scaleY(1F).start() super.clearView(recyclerView, viewHolder) } override fun isLongPressDragEnabled(): Boolean { return false } override fun isItemViewSwipeEnabled(): Boolean { return isSwipe } }

3. ItemTouchHelper.Callback() 重要方法说明

3.1 getMovementFlags():Int{}
实现此方法获取移动的标志,方法内部需要调用makeMovementFlags(dragFlags, swipeFlags)方法设置拖拽和滑动的标志

该标志定义每个状态下启用的移动方向

// 这个方法用于让RecyclerView拦截向上滑动,向下滑动,想左滑动 // makeMovementFlags(dragFlags, swipeFlags); dragFlags显示项目支持拖动的方向 swipe标记物品可以滑动的方向 0 表示禁止 // 网格布局:上下左右 // 线性布局:上下/左右 override fun getMovementFlags( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { //拖动标记 0 表示禁止 var dragFlags = 0 //侧滑标记 var swipeFlags = 0 var layoytManager = recyclerView.layoutManager if (layoytManager is GridLayoutManager) { //网格布局 默认禁止侧滑 if (viewHolder.adapterPosition != mAdapter.limitPosition) { dragFlags =ItemTouchHelper.LEFT or ItemTouchHelper.UP or ItemTouchHelper.RIGHT or ItemTouchHelper.DOWN } return makeMovementFlags(dragFlags, swipeFlags) } else if (layoytManager is LinearLayoutManager) { //处理 禁止滑动模块得逻辑 禁止移动得模块 禁止左右滑动 if (viewHolder.adapterPosition != mAdapter.limitPosition) { dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END } return makeMovementFlags(dragFlags, swipeFlags) } //默认 禁止滑动 禁止拖拽 return makeMovementFlags(dragFlags, swipeFlags) }

3.2 makeMovementFlags(dragFlags, swipeFlags)

创建移动标志的便捷方法,通过此方法创建移动的标志.dragflag显示项目可以拖动的方向,swipe标记物品可以滑动的方向。返回由给定拖动和滑动标志组成的整数。

//源码 public static int makeMovementFlags(int dragFlags, int swipeFlags) { return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags) | makeFlag(ACTION_STATE_SWIPE, swipeFlags) | makeFlag(ACTION_STATE_DRAG, dragFlags); }

3.3 override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,target: RecyclerView.ViewHolder): Boolean {}

Item拖动的项从其旧位置移动到新位置时调用onMove()方法,在这里我们可以处理移动数据的刷新(重新排序数据)

override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder ): Boolean { // 起始位置 val fromPosition = viewHolder.adapterPosition // 结束位置 val toPosition = target.adapterPosition // 固定位置 处理笃定位置不要移动 if (fromPosition == mAdapter.limitPosition || toPosition == mAdapter.limitPosition) { return false } // 根据滑动方向 交换数据 for 循环不包含 toPosition 1 替代2 2替代3 3 替代4 if (fromPosition < toPosition) { // 含头不含尾 for (index in fromPosition until toPosition) { //交换集合得位置 Collections.swap(mData, index, index + 1) } } else { // 含头不含尾 for (index in fromPosition downTo toPosition + 1) { Collections.swap(mData, index, index - 1) } } // 刷新布局 mAdapter.notifyItemMoved(fromPosition, toPosition) return true }

3.4 拖拽/滑动状态的处理

override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { if (viewHolder == null) return //空闲状态 if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) { ViewCompat.animate(viewHolder!!.itemView).setDuration(100).scaleX(1.2F).scaleY(1.2F) .start() } super.onSelectedChanged(viewHolder, actionState) } override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) { // 恢复显示 // 这里不能用if判断,因为GridLayoutManager是LinearLayoutManager的子类,改用when,类型推导有区别 ViewCompat.animate(viewHolder.itemView).setDuration(100).scaleX(1F).scaleY(1F).start() super.clearView(recyclerView, viewHolder) }

注意:

actionState表示Item的状态

  • ItemTouchHelper.ACTION_STATE_IDLE 空闲状态。
  • ItemTouchHelper.ACTION_STATE_SWIPE 滑动状态。
  • ItemTouchHelper.ACTION_STATE_DRAG 拖拽状态。

3.5 滑动/拖拽的开关

override fun isLongPressDragEnabled(): Boolean { return false } override fun isItemViewSwipeEnabled(): Boolean { return isSwipe }

总结

ItemTouchHelper是一个实用系统类,用于向RecyclerView添加滑动解除和拖放支持.通过这个类通过ItemTouchHelper.Callback() 回调帮助开发者实现了拖拽和滑动的效果

写作不易欢迎点赞

源码

DragRecycle源码

发布人:e6dc****    IP:117.173.23.***     举报/删稿
展会推荐
让朕来说2句
评论
收藏
点赞
转发