ItemTouchHelper.SimpleCallBack limit width of swipe - android

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.

Related

RecyclerView item dragging while keeping background visible

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.

[RecyclerView & ItemTouchHelper]After setPadding to RecyclerView, it fail to drag item?

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 :(

Kotlin - Scroll Detection issue in Recyclerview

Using below code to check that whether RecyclerView reached to bottom or not..
Means Checking that last item of the Recyclerview is visible or not..
For that I have googled and added Scroll listener in Recyclerview.
Using below code:
MyRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (!MyRecyclerView.canScrollVertically(1)) {
Toast.makeText(mContext, "Last", Toast.LENGTH_LONG).show();
}
}
})
Here, I am trying to check that Recyclerview reached to bottom or not.
Toast is not displaying when I scroll recyclerview to the bottom.
But, Toast displayed suddenly when items in the recyclerview binds first time. It should toast only when user scroll up to the bottom of the Recyclerview.
What might be the issue ?
Please guide. Thanks.
I think, using RecyclerView.LayoutManager api will better suit your needs.
You can check LinearLayoutManager::findLastCompletelyVisibleItemPosition or findLastVisibleItemPosition method in your RecyclerView.OnScrollListener
rvPlayers?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy > 0) {
if (isLastVisable()) {
//code here
}
}
}
})
private fun isLastVisable(): Boolean {
val layoutManager = rvPlayers.layoutManager as LinearLayoutManager
val pos = layoutManager.findLastCompletelyVisibleItemPosition()
val numItems = adapter.itemCount
return (pos >= numItems - 1)
}

Android - RecyclerView Swipe Animation not going away

I have setup a swipe gesture on the recyclerView item in order to edit it. I've done it like this:
private val swipeHelper = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition //get position which is swipe
if (direction == ItemTouchHelper.RIGHT) {
… // do stuff
}
}
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
val icon: Bitmap
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
val itemView = viewHolder.itemView
val height: Float = (itemView.bottom.toFloat() - itemView.top.toFloat())
val width = height / 3
if (dX > 0) {
paint.color = Color.parseColor("#FF9300")
val background = RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
c.drawRect(background, paint)
icon = BitmapFactory.decodeResource(resources, R.drawable.pencil)
val iconDest = RectF(
itemView.left.toFloat() + width,
itemView.top.toFloat() + width,
itemView.left.toFloat() + 2*width,
itemView.bottom.toFloat() - width)
c.drawBitmap(icon, null, iconDest, paint)
}
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
}
}
The problem I am facing, and that I cannot figure out how to resolve is that after the swipe, since I am not deleting the element, the animatino won't reset until I completely restart the activity.
Here is an explanatory image:
Has anyone ever dealt with this problem? Any Idea on how to fix it without having to restart the whole activity?
Call notifyItemChanged on you adapter. This will reset the adapter.
You can Do this in your Activity by calling notifyDatasetChanged(); or notifyItemChanged(); :
ItemTouchHelper.SimpleCallback callback = new RecyclerItemTouchHelper(this, 0, ItemTouchHelper.LEFT
, new RecyclerItemTouchHelper.RecyclerItemTouchHelperListener() {
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, final int position) {
if (direction == ItemTouchHelper.LEFT) {
// do what you want here and after that call the function below:
recyclerAdapter.notifyItemChanged(position);
}
}
});
new ItemTouchHelper(callback).attachToRecyclerView(recyclerView);

Drag Shadow Using Item Touch Helper android

ItemTouchhelper Class in android helps only to move the entire view around the recyclerview. Is it possible to make a shadow of a view to be dragged(The original view to be in its place-fixed) using the Item touchHelper class?
I overrode onChildDraw and made my own shadow
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
if (isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) {
// make shadown
isRotated = true;
}
} else {
// view is going back to orig
if (isRotated) {
// undo shadow
}
}
}
Here's a simple way to achieve shadow on the dragged item (for API >= 21 since it relies on elevation).
Declare an attribute in attrs.xml:
<attr name="draggedItemElevation" format="dimension"/>
Set the attribute value in your theme:
<style name="AppTheme" parent="...">
<item name="draggedItemElevation">2dp</item>
</style>
Use this callback for the ItemTouchHelper:
class DragItemTouchHelperCallback : ItemTouchHelper.Callback() {
// Get elevation in pixels from ?attr/draggedItemElevation.
private val elevation = context.obtainStyledAttributes(
intArrayOf(R.attr.editDraggedItemElevation)).use {
it.getDimensionPixelSize(0, 0).toFloat()
}
override fun onChildDraw(c: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder,
dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean) {
val view = viewHolder.itemView
view.translationX = dX
view.translationY = dY
if (isCurrentlyActive) {
ViewCompat.setElevation(view, elevation)
}
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
val view = viewHolder.itemView
view.translationX = 0f
view.translationY = 0f
ViewCompat.setElevation(view, 0f)
}
override fun getMovementFlags(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder): Int {
// ...
}
override fun onMove(recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder): Boolean {
// ...
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
// ...
}
}
This will temporarily change the dragged item elevation to ?attr/draggedItemElevation, then set it back to 0 when the item is dropped.
So, you just want a "preview" drag with a shadow...?
A DragShadow is easily achieved, when you call startDrag:
view.startDrag(clipData, new View.DragShadowBuilder(v), null, 0); //clipData can be null.
For an excellent tutorial on how to handle Drag & Drop, I recommend the one from Paul Burke. He defines a background, to show the user on which position the view will be placed.
If this does not answer your question, please provide more detail in your question.

Categories

Resources