Android 在拖动时,如何在使用ItemTouchHelper时取消在RecyclerView中拖动项目? 背景

Android 在拖动时,如何在使用ItemTouchHelper时取消在RecyclerView中拖动项目? 背景,android,android-recyclerview,drag-and-drop,itemtouchhelper,Android,Android Recyclerview,Drag And Drop,Itemtouchhelper,我正在尝试创建一个具有不同视图类型的RecyclerView,它还具有拖放功能,以及单击和长单击操作 它类似于手机应用程序,您可以更改收藏夹项目的顺序。在手机应用程序中,当您长时间触摸某个项目时,会立即显示关联菜单,如果您继续拖动,关联菜单将消失 然而,在这种情况下,我需要做相反的事情。长按时,如果用户在很短的时间内没有拖动,或者如果用户停止长按而没有拖动,我们会在屏幕上显示一个对话框,我需要停止拖动过程 问题 虽然我成功地处理了长触摸机制,并显示了关于这些特殊情况的对话框,但我未能使拖动停止

我正在尝试创建一个具有不同视图类型的RecyclerView,它还具有拖放功能,以及单击和长单击操作

它类似于手机应用程序,您可以更改收藏夹项目的顺序。在手机应用程序中,当您长时间触摸某个项目时,会立即显示关联菜单,如果您继续拖动,关联菜单将消失

然而,在这种情况下,我需要做相反的事情。长按时,如果用户在很短的时间内没有拖动,或者如果用户停止长按而没有拖动,我们会在屏幕上显示一个对话框,我需要停止拖动过程

问题 虽然我成功地处理了长触摸机制,并显示了关于这些特殊情况的对话框,但我未能使拖动停止

这意味着,如果用户即使在对话框出现后仍继续触摸屏幕,仍可以继续拖动:

整个代码都可用(没有长触摸行为的代码可用),但主要代码如下:

class MainActivity : AppCompatActivity() {
    sealed class Item(val id: Long, val itemType: Int) {
        class HeaderItem(id: Long) : Item(id, ITEM_TYPE_HEADER)
        class NormalItem(id: Long, val data: Long) : Item(id, 1)
    }

    enum class ItemActionState {
        IDLE, LONG_TOUCH_OR_SOMETHING_ELSE, DRAG, SWIPE, HANDLED_LONG_TOUCH
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        val items = ArrayList<Item>(100)
        var itemDataCounter = 0L
        items.add(Item.HeaderItem(0L))
        for (i in 0 until 100) {
            items.add(Item.NormalItem(itemDataCounter, itemDataCounter))
            ++itemDataCounter
        }
        val gridLayoutManager = recyclerView.layoutManager as GridLayoutManager
        gridLayoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
            override fun getSpanSize(position: Int): Int {
                return when (recyclerView.adapter!!.getItemViewType(position)) {
                    ITEM_TYPE_HEADER -> gridLayoutManager.spanCount
                    ITEM_TYPE_NORMAL -> 1
                    else -> throw Exception("unknown item type")
                }
            }
        }
        recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            init {
                setHasStableIds(true)
            }

            override fun getItemViewType(position: Int): Int = items[position].itemType

            override fun getItemId(position: Int): Long = items[position].id

            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
                val view = when (viewType) {
                    ITEM_TYPE_HEADER -> LayoutInflater.from(parent.context).inflate(R.layout.header_item, parent, false)
                    ITEM_TYPE_NORMAL -> LayoutInflater.from(parent.context).inflate(R.layout.grid_item, parent, false)
                    else -> throw Exception("unknown item type")
                }
                return object : RecyclerView.ViewHolder(view) {}
            }

            override fun getItemCount() = items.size

            override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                when (getItemViewType(position)) {
                    ITEM_TYPE_NORMAL -> {
                        val data = (items[position] as Item.NormalItem).data
                        holder.itemView.setBackgroundColor(when (data % 4L) {
                            0L -> 0xffff0000.toInt()
                            1L -> 0xffffff00.toInt()
                            2L -> 0xff00ff00.toInt()
                            else -> 0xff00ffff.toInt()
                        })
                        holder.itemView.textView.text = "item $data"
                    }
                    ITEM_TYPE_HEADER -> {
                    }
                    else -> throw Exception("unknown item type")
                }
            }
        }
        val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
            val touchSlop = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1f, resources.displayMetrics)
            //            val touchSlop = ViewConfiguration.get(this@MainActivity).scaledTouchSlop
            val longTouchTimeout = ViewConfiguration.getLongPressTimeout() * 2
            var touchState: ItemActionState = ItemActionState.IDLE
            var lastViewHolderPosHandled: Int? = null
            val handler = Handler()
            val longTouchRunnable = Runnable {
                if (lastViewHolderPosHandled != null && touchState == ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE) {
                    //                    Log.d("AppLog", "timer timed out to trigger long touch")
                    onItemLongTouch(lastViewHolderPosHandled!!)
                }
            }

            private fun onItemLongTouch(pos: Int) {
                //                Log.d("AppLog", "longTouchTimeout:$longTouchTimeout")
                val item = items[pos] as Item.NormalItem
                //                Toast.makeText(this@MainActivity, "long touch on :$pos ", Toast.LENGTH_SHORT).show()
                AlertDialog.Builder(this@MainActivity).setTitle("long touch").setMessage("long touch on pos: $pos - item ${item.data}").show()
                touchState = ItemActionState.HANDLED_LONG_TOUCH
                lastViewHolderPosHandled = null
                handler.removeCallbacks(longTouchRunnable)
            }

            override fun onChildDrawOver(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder?, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
                super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
                //                Log.d("AppLog", "onChildDrawOver $dX $dY pos:${viewHolder?.adapterPosition} actionState:$actionState isCurrentlyActive:$isCurrentlyActive")
                if (touchState == ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE && (dX >= touchSlop || dY >= touchSlop)) {
                    lastViewHolderPosHandled = null
                    handler.removeCallbacks(longTouchRunnable)
                    touchState = if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) ItemActionState.DRAG else ItemActionState.SWIPE
                    Log.d("AppLog", "decided it's not a long touch, but $touchState instead")
                }
            }

            override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
                super.onSelectedChanged(viewHolder, actionState)
                //                Log.d("AppLog", "onSelectedChanged adapterPosition: ${viewHolder?.adapterPosition} actionState:$actionState")
                when (actionState) {
                    ItemTouchHelper.ACTION_STATE_IDLE -> {
                        //user finished drag or long touch
                        if (touchState == ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE)
                            onItemLongTouch(lastViewHolderPosHandled!!)
                        touchState = ItemActionState.IDLE
                        handler.removeCallbacks(longTouchRunnable)
                        lastViewHolderPosHandled = null
                    }
                    ItemTouchHelper.ACTION_STATE_DRAG, ItemTouchHelper.ACTION_STATE_SWIPE -> {
                        if (touchState == ItemActionState.IDLE) {
                            lastViewHolderPosHandled = viewHolder!!.adapterPosition
                            //                            Log.d("AppLog", "setting timer to trigger long touch")
                            handler.removeCallbacks(longTouchRunnable)
                            //started as long touch, but could also be dragging or swiping ...
                            touchState = ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE
                            handler.postDelayed(longTouchRunnable, longTouchTimeout.toLong())
                        }
                    }
                }
            }

            override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
                //                Log.d("AppLog", "onMove")
                if (touchState == ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE) {
                    lastViewHolderPosHandled = null
                    handler.removeCallbacks(longTouchRunnable)
                    touchState = ItemActionState.DRAG
                }
                if (viewHolder.itemViewType != target.itemViewType)
                    return false
                val fromPosition = viewHolder.adapterPosition
                val toPosition = target.adapterPosition
                //                val item = items.removeAt(fromPosition)
                //                recyclerView.adapter!!.notifyItemRemoved(fromPosition)
                //                items.add(toPosition, item)
                //                recyclerView.adapter!!.notifyItemInserted(toPosition)
                Collections.swap(items, fromPosition, toPosition)
                recyclerView.adapter!!.notifyItemMoved(fromPosition, toPosition)
                //                recyclerView.adapter!!.notifyDataSetChanged()
                return true
            }

            override fun isLongPressDragEnabled(): Boolean = true

            override fun isItemViewSwipeEnabled(): Boolean = false

            override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
                if (viewHolder.itemViewType == ITEM_TYPE_HEADER)
                    return makeMovementFlags(0, 0)
                //                Log.d("AppLog", "getMovementFlags")
                val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
                val swipeFlags = if (isItemViewSwipeEnabled) ItemTouchHelper.START or ItemTouchHelper.END else 0
                return makeMovementFlags(dragFlags, swipeFlags)
            }

            override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                if (touchState == ItemActionState.LONG_TOUCH_OR_SOMETHING_ELSE) {
                    lastViewHolderPosHandled = null
                    handler.removeCallbacks(longTouchRunnable)
                    touchState = ItemActionState.DRAG
                }
                val position = viewHolder.adapterPosition
                items.removeAt(position)
                recyclerView.adapter!!.notifyItemRemoved(position)
            }
        })
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        var url: String? = null
        when (item.itemId) {
            R.id.menuItem_all_my_apps -> url = "https://play.google.com/store/apps/developer?id=AndroidDeveloperLB"
            R.id.menuItem_all_my_repositories -> url = "https://github.com/AndroidDeveloperLB"
            R.id.menuItem_current_repository_website -> url = "https://github.com/AndroidDeveloperLB/RecyclerViewDragAndDropTest"
        }
        if (url == null)
            return true
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
        @Suppress("DEPRECATION")
        intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
        startActivity(intent)
        return true
    }

    companion object {
        const val ITEM_TYPE_HEADER = 0
        const val ITEM_TYPE_NORMAL = 1
    }
}
class MainActivity:AppCompatActivity(){
密封类项目(val id:Long,val itemType:Int){
类标题项(id:Long):项(id,项类型\u标题)
类NormalItem(id:Long,val数据:Long):项(id,1)
}
枚举类ItemActionState{
空闲、长时间触摸或其他,拖动、滑动、处理长时间触摸
}
重写创建时的乐趣(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
设置支持操作栏(工具栏)
val项目=阵列列表(100)
var itemDataCounter=0L
项目.添加(项目.标题项(0L))
对于(i在0到100之间){
items.add(Item.NormalItem(itemDataCounter,itemDataCounter))
++itemDataCounter
}
val gridLayoutManager=recyclerView.layoutManager作为gridLayoutManager
gridLayoutManager.spanSizeLookup=对象:gridLayoutManager.spanSizeLookup(){
覆盖大小(位置:Int):Int{
返回时间(recyclerView.adapter!!.getItemViewType(位置)){
项目类型标题->gridLayoutManager.spanCount
项目类型正常->1
else->抛出异常(“未知项目类型”)
}
}
}
recyclerView.adapter=对象:recyclerView.adapter(){
初始化{
SetHassTableId(真)
}
重写getItemViewType(位置:Int):Int=items[position].itemType
重写getItemId(位置:Int):Long=items[position].id
override fun onCreateViewHolder(父级:ViewGroup,viewType:Int):RecyclerView.ViewHolder{
val视图=何时(视图类型){
项目\u类型\u标题->LayoutFlater.from(parent.context).充气(R.layout.HEADER\u项目,parent,false)
项目\u类型\u正常->LayoutFlater.from(parent.context).充气(R.layout.grid\u项目,parent,false)
else->抛出异常(“未知项目类型”)
}
返回对象:RecyclerView.ViewHolder(视图){}
}
重写getItemCount()=items.size
覆盖onBindViewHolder(holder:RecyclerView.ViewHolder,位置:Int){
何时(getItemViewType(位置)){
项目类型正常->{
val数据=(项[位置]作为项.NormalItem)。数据
holder.itemView.setBackgroundColor(数据%4L时){
0L->0xffff0000.toInt()
1L->0xffffff00.toInt()
2L->0xff00ff00.toInt()
else->0xff00ffff.toInt()
})
holder.itemView.textView.text=“item$data”
}
项目类型标题->{
}
else->抛出异常(“未知项目类型”)
}
}
}
val itemTouchHelper=itemTouchHelper(对象:itemTouchHelper.Callback(){
val touchSlop=TypedValue.applyDimension(TypedValue.COMPLEX\u UNIT\u DIP,1f,resources.displayMetrics)
//val touchlop=ViewConfiguration.get(this@MainActivity)扇贝
val longTouchTimeout=ViewConfiguration.getLongPressTimeout()*2
var touchtate:ItemActionState=ItemActionState.IDLE
var lastViewHolderPoshanded:Int?=null
val handler=handler()
val longTouchRunnable=Runnable{
if(lastViewHolderPoshanded!=null&&touchState==ItemActionState.LONG\u TOUCH\u或其他内容){
//Log.d(“AppLog”,“计时器超时以触发长时间触摸”)
onItemLongTouch(LastViewHolderPostHandled!!)
}
}
私密娱乐网(位置:Int){
//Log.d(“AppLog”、“longTouchTimeout:$longTouchTimeout”)
val项目=项目[pos]作为项目。NormalItem
//Toast.makeText(this@MainActivity,“长触:$pos”,Toast.LENGTH\u SHORT.show()
AlertDialog.Builder(this@MainActivity).setTitle(“长触摸”).setMessage(“pos上的长触摸:$pos-item${item.data}”).show()
touchState=ItemActionState.HANDLED\u LONG\u TOUCH
LastViewHolderPostHandled=null
handler.removeCallbacks(LongtouchRunName)
}
覆盖趣味OnChildRawOver(c:Canvas、recyclerView:recyclerView、viewHolder:recyclerView.viewHolder?、dX:Float、dY:Float、actionState:Int、isCurrentlyActive:Boolean){
override fun isLongPressDragEnabled() :Boolean {
    return false;
}
/**
     * Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a
     * View is long pressed. You can disable that behavior via
     * {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}.