[英]How to cancel dragging of items in RecyclerView when using ItemTouchHelper, as you drag?
I'm trying to have a RecyclerView that has different view types, and yet has the ability for drag&drop, together with single click and long click operations.我正在尝试拥有一个具有不同视图类型的 RecyclerView,但具有拖放功能以及单击和长按操作的能力。
It's similar to what you have on Phone app, where you can change the order of your favorites items.它类似于您在电话应用程序上的功能,您可以在其中更改收藏夹项目的顺序。 On the Phone app, when you long touch an item, a context menu appears right away, and if you continue to drag, the context menu is gone.在电话应用程序中,当您长按某个项目时,会立即出现一个上下文菜单,如果您继续拖动,上下文菜单就会消失。
However, in this case, I'm required to do the opposite.但是,在这种情况下,我需要做相反的事情。 Upon long pressing, if the user hasn't dragged in a very short time or if the user has stopped long pressing without dragging, we show a dialog on the screen, and I'm required to stop the dragging procedure.长按时,如果用户在很短的时间内没有拖动,或者用户在没有拖动的情况下停止长按,我们会在屏幕上显示一个对话框,我需要停止拖动过程。
While I've succeeded handling the long touch mechanism, and I show a dialog on these special cases, I failed to cause the dragging to stop.虽然我已经成功地处理了长触摸机制,并且我在这些特殊情况下显示了一个对话框,但我未能使拖动停止。
This means, that if the user keeps touching the screen even after the dialog appears, it is still possible to keep dragging:这意味着,如果用户在对话框出现后继续触摸屏幕,仍然可以继续拖动:
The entire code is available here (code without the long touch behavior available here ), but here's the main code:整个代码在这里可用(没有长触摸行为的代码在这里可用),但这里是主要代码:
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
}
}
I tried to look at all of the documentation of RecyclerView and ItemTouchHelper.我试图查看 RecyclerView 和 ItemTouchHelper 的所有文档。 Also tried to look for similar questions here and over the Internet.还试图在这里和互联网上寻找类似的问题。
I can't see any way to tell the dragging mechanism: "I'm done with dragging now, cancel the dragging".我看不出有什么方法可以告诉拖动机制:“我现在已经完成了拖动,取消拖动”。
How can I cancel the dragging that's initiated and maintained by ItemTouchHelper?如何取消由 ItemTouchHelper 发起和维护的拖动?
Override the isLongPressDragEnabled
method with false
:用false
覆盖isLongPressDragEnabled
方法:
override fun isLongPressDragEnabled() :Boolean {
return false;
}
Source来源
https://android.googlesource.com/platform/frameworks/support/+/c045910/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java https://android.googlesource.com/platform/frameworks/support/+/c045910/v7/recyclerview/src/android/support/v7/widget/helper/ItemTouchHelper.java
/**
* 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()}.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.