I am trying to port some iOS functionality to Android.
I intent to create a table where on swipe to the left shows 2 button: Edit and Delete.
I have been playing with it and I know I am very close. The secret really lies on the method OnChildDraw.
I would like to Draw a Rect that fits the text Delete then draw the Edit text besides it with their respective background color. The remaining white space when clicked should restore the row to its initial position.
I have managed to paint the background while the user is swiping to the sides but I don't know how to add the listeners and once it is swiped to the side, the dragging function begins to misbehave.
I am working on Xamarin but pure java solutions also are accepted as I can easily port them to c#.
public class SavedPlacesItemTouchHelper : ItemTouchHelper.SimpleCallback
{
private SavedPlacesRecyclerviewAdapter adapter;
private Paint paint = new Paint();
private Context context;
public SavedPlacesItemTouchHelper(Context context, SavedPlacesRecyclerviewAdapter adapter) : base(ItemTouchHelper.ActionStateIdle, ItemTouchHelper.Left)
{
this.context = context;
this.adapter = adapter;
}
public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
{
return false;
}
public override void OnSwiped(RecyclerView.ViewHolder viewHolder, int direction)
{
}
public override void OnChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, bool isCurrentlyActive)
{
float translationX = dX;
View itemView = viewHolder.ItemView;
float height = (float)itemView.Bottom - (float)itemView.Top;
if (actionState == ItemTouchHelper.ActionStateSwipe && dX <= 0) // Swiping Left
{
translationX = -Math.Min(-dX, height * 2);
paint.Color = Color.Red;
RectF background = new RectF((float)itemView.Right + translationX, (float)itemView.Top, (float)itemView.Right, (float)itemView.Bottom);
c.DrawRect(background, paint);
//viewHolder.ItemView.TranslationX = translationX;
}
else if (actionState == ItemTouchHelper.ActionStateSwipe && dX > 0) // Swiping Right
{
translationX = Math.Min(dX, height * 2);
paint.Color = Color.Red;
RectF background = new RectF((float)itemView.Right + translationX, (float)itemView.Top, (float)itemView.Right, (float)itemView.Bottom);
c.DrawRect(background, paint);
}
base.OnChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
}
}
This is what I currently have.
If you know how to add listeners or any suggestions please leave a comment!
UPDATE:
I just realized that on double tap on the white remaining space of the row already restore the row to its initial state. Not a single tap though :(
I have a much simpler solution:
in onChildDraw(), just increase its width by the dX value.
Make sure not to call the default super.onChildDraw()
I struggled with the same issue, and tried to find a solution online. Most of the solutions use a two-layer approach (one layer view item, another layer buttons), but I want to stick with ItemTouchHelper only. At the end, I came up with a worked solution. Please check below.
Usage:
Note: This helper class is designed for left swipe. You can change swipe direction in SwipeHelper's constructor, and making changes based on dX in onChildDraw method accordingly.
If you want to show image in the button, just make the use of imageResId in UnderlayButton, and re-implement the onDraw method.
There is a known bug, when you swipe an item diagonally from one item to another, the first touched item will flash a little. This could be addressed by decreasing the value of getSwipeVelocityThreshold, but this makes harder for user to swipe the item. You can also adjust the swiping feeling by changing two other values in getSwipeThreshold and getSwipeEscapeVelocity. Check into the ItemTouchHelper source code, the comments are very helpful.
I believe there is a lot place for optimization. This solution just gives an idea if you want to stick with ItemTouchHelper. Please let me know if you have problem using it. Below is a screenshot.
Acknowledgment: this solution is mostly inspired from AdamWei's answer in this post
If you want button(s) on left side as well when swipe in the other direction, just try to add this simple lines in the existing answer:
In drawButtons method:
private void drawButtons(Canvas c, View itemView, List buffer, int pos, float dX) { float right = itemView.getRight(); float left = itemView.getLeft(); float dButtonWidth = (-1) * dX / buffer.size();
In onDraw method check the value of dX and set text and colour of buttons:
public void onDraw(Canvas c, RectF rect, int pos, float dX) { Paint p = new Paint();
If you use a
RecyclerView
, try to useOnScrollListener
. Do something like this.Following Wenxi Zeng's answer here, if you want to have the text in the buttons on multiple lines, replace UnderlayButton's onDraw method with this:
I wanted to use this touch gesture in my app too, after working too much with Itemtouchhelper I decided to write my own touch handler:
Note: Set this as ontouchlistener of your viewholder content when creating the viewholder. You can add your animations to return the item to its first place.
You can also write your custom layoutmanager to block vertical scroll while item is sliding.