1.itemTouchHelper works well at first.
Kotlin code as followed
val itemTouchHelper = ItemTouchHelper(object: ItemTouchHelper.Callback(){
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder:RecyclerView.ViewHolder): Int{
val dragFlag = ItemTouchHelper.UP | ItemTouchHelper.Down
val swipeFlags = 0
return makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder){
val fromPosition = viewHolder.adapterPosition
val toPosition = target.adapterPosition
Collections.swap(mList, fromPosition, toPosition)
mAdapter.notifyItemMoved(fromPosition, toPosition)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int){
}
})
itemTouchHelper.attachToRecyclerView(mRecyclerView)
2.if I setPadding to RecyclerView, there is something wrong when I try to drag item
mRecyclerView.setPadding(0,250,0,0) // (left, top, right ,bottom)
we can drag items easily when recyclerview is at its top(recyclerview has not been scrolled, it is at its top). However, if the recyclerview is not at its top(recyclerview has been scrolled downwards), when I drag the item it will go to the toppest position immediately instead of the position I drag to.
watch the gif for more details, somebody help me :(
Related
I have RecyclerView Drag & Drop feature, but I'd like to do some calculations onDrop. When I put my expensiveFunction() in onMove() it's triggered at every position change until the drag is over. That's a big overkill. Is there a way to trigger function on drag end?
val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
private var simpleCallback = object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP.or(ItemTouchHelper.DOWN), 0) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
val startPosition = viewHolder.absoluteAdapterPosition
val endPosition = target.absoluteAdapterPosition
Collections.swap(itemList, startPosition, endPosition)
recyclerView.adapter?.notifyItemMoved(startPosition, endPosition)
expensiveFunction()
return true
}
}
You could override onSelectedChanged() which get called when the ViewHolder swiped or dragged by the ItemTouchHelper.
To catch the drop action examine the actionState value to be ItemTouchHelper.ACTION_STATE_IDLE:
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
when (actionState) {
// when the item is dropped
ItemTouchHelper.ACTION_STATE_IDLE -> {
Log.d(TAG, "Item is dropped")
}
}
}
RecyclerView drag & drop works perfectly by attaching ItemTouchHelper to RecyclerView. Like below:
abstract class RecyclerViewDragDetector : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT, 0) {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
viewHolder?.itemView?.alpha = 0.5f
}
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
viewHolder.itemView.alpha = 1.0f
}
}
I am able to drag & drop items using above code, but the problem is while dragging an item, that position remains blank just like empty. My requirement is to keep a background place holder visible while dragging an item.
I want something like below image.
Any suggestion is really really appreciated.
I am trying to build a swipe option in RecyclerView item. The problem is that, When I swipe to the right the whole item is gone. This is not how I want it to be.
This is the example which I want the swipe action to work. When the user swipes to right after particular threshold I want the swipe width to stop and when the user release, I want it to go back.
Currently the user can swipe to the right more than a particular width as shown in the first gif file. I want to limit that and make sure that the user can only swipe to a certain limit and when the user releases it goes back.
Current Code:
val myCallback = object: ItemTouchHelper.SimpleCallback(0,
ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean = false
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
super.onChildDraw(c, recyclerView, viewHolder,
dX , dY , actionState, isCurrentlyActive)
c.clipRect(0f, viewHolder.itemView.top.toFloat(),
dX , viewHolder.itemView.bottom.toFloat())
if(dX < c.width / 3)
c.drawColor(Color.GRAY)
else
c.drawColor(Color.RED)
val textMargin = resources.getDimension(R.dimen.text_margin)
.roundToInt()
trashBinIcon.bounds = Rect(
textMargin,
viewHolder.itemView.top + textMargin,
textMargin + trashBinIcon.intrinsicWidth,
viewHolder.itemView.top + trashBinIcon.intrinsicHeight
+ textMargin
)
trashBinIcon.draw(c)
}
}
I tried ItemTouchHelper : Limit swipe width of ItemTouchHelper.SimpleCallBack on RecyclerView solution but didn't work.
I use RecyclerView's ItemTouchHelper to change the order(drag & drop),And I want to move only a part of the list.
I don't want to move after a certain index. How can I handle it with one RecyclerView?
If I explain a little more about what I want to make,
The active items are sorted up and the non-active items are sorted down. And only the activated items will shift the order.
Right now, when I drag an active item, it goes down to the area of the non-active item, but I want to prevent it from going down. How can I do it?
UPDATE
open class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) :
ItemTouchHelper.Callback() {
private var isLongPressDrag = true
fun setLongPressDragEnable(isLongPressDrag: Boolean) {
this.isLongPressDrag = isLongPressDrag
}
override fun isLongPressDragEnabled() = isLongPressDrag
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val swipeFlags = 0
return makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return mAdapter.onItemMove(viewHolder.adapterPosition, target.adapterPosition)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, i: Int) {
mAdapter.onItemDismiss(viewHolder.adapterPosition)
}
}
Use ItemTouchHelper.SimpleCallback and override getDragDirs and onMove methods:
ItemTouchHelper.SimpleCallback(
UP or DOWN,
0
) {
override fun getDragDirs(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
val selectedPosition = viewHolder.adapterPosition
val isActive: Boolean = // retrieve your model from list and check its active state
return if (isActive) super.getDragDirs(recyclerView, viewHolder) else 0
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder,
): Boolean {
val toPosition = target.adapterPosition
val isActiveTarget: Boolean = // retrieve your target model from list and check its active state
if (!isActiveTarget) return false
val fromPosition = viewHolder.adapterPosition
moveItem(fromPosition, toPosition)
return true
}
}
Returning 0 on getDragDirs prevents inactive item selection, while returning false on onMove prevents item movement while dragging.
I've created drag and drop for my recycler view, but I want disable drag and drop option on pull of cells (it's a headers in my view). How to make them not available for drag and drop function?
Drag And drop helper
class SimpleItemTouchHelperCallback(private val mAdapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() {
override fun isLongPressDragEnabled() = true
override fun isItemViewSwipeEnabled() = true
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val swipeFlags = ItemTouchHelper.START or ItemTouchHelper.END
return ItemTouchHelper.Callback.makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
mAdapter.onItemMove(viewHolder.adapterPosition, target.adapterPosition)
return true
}
}
Adapter for Recyclerview
interface ItemTouchHelperAdapter {
fun onItemMove(fromPosition: Int, toPosition: Int): Boolean
}
Code to pair adapter to rv
val drugAndDropHandler = SimpleItemTouchHelperCallback(adapter)
ItemTouchHelper(drugAndDropHandler).attachToRecyclerView(multiple_stores_list)
Just need to override getMovementFlags
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
if (viewHolder.adapterPosition in 0..NOT_DRAGABLE_ITEMS_MAX_POSITION) {
return 0
}
}