Like those notifications that you can't dismiss: you swipe till the middle, then it stops. Does anyone knows how to make this in a RecyclerView?
If you can't remember:
https://youtu.be/eFvhFkZfGlA
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return 1;
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
The key part here is the .getSwipeThreshold
method
----------------------------------
EDIT: Actually, there is a lot more to it and I could not find a way to exactly implement what is asked(exactly like the video and the Android notification bar), but since I had a wrong answer here I decided to edit my post and implement something very similar to it.
There are other methods to be overridden in addition to what is mentioned and fields to be added in the ItemTouchHelper.SimpleCallback
class.
Fields:
private final float stopPoint = 200; // The point where items stop when move past it
private final float swipeStopFraction = 0.002f; // higher means items stop sooner when swiped
private final float swipeSpeedFraction = 0.8f; // lower means items move slower when swiped
private float prevA = 1;
private float a = 1;
private float prevDX;
private float backDX;
private boolean leftLock = false;
private boolean rightLock = false;
private boolean leftExpanded = false;
private boolean rightExpanded = false;
private CustomRecyclerViewAdapter.CustomViewHolder prevHolder;
Other methods:
@Override
public float getSwipeVelocityThreshold(float defaultValue) {
return 0;
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
final CustomRecyclerViewAdapter.CustomViewHolder holder = (CustomRecyclerViewAdapter.CustomViewHolder) viewHolder;
prevHolder = holder;
dX *= swipeSpeedFraction;
if (!isCurrentlyActive) {
if (!leftLock) {
leftExpanded = false;
}
if (!rightLock) {
rightExpanded = false;
}
a = 0;
}
if (isCurrentlyActive) {
if (dX > 0 && !rightExpanded) {
leftExpanded = true;
}
if (dX < 0 && !leftExpanded) {
rightExpanded = true;
}
}
float cachedDX = dX;
if (a == 0) {
if (dX / prevA > stopPoint && !rightExpanded && backDX == 0) {
leftLock = true;
leftExpanded = true;
}
if (dX / prevA < -stopPoint && !leftExpanded && backDX == 0) {
rightLock = true;
rightExpanded = true;
}
if (backDX != 0) {
dX += backDX - dX;
backDX /= 2;
} else {
dX /= prevA;
}
a = 1;
} else if (dX == 0) {
backDX = 0;
} else if (dX > 0) {
if (leftExpanded) {
if (backDX != 0) {
leftLock = true;
}
dX /= a;
prevA = a;
a += swipeStopFraction * (cachedDX - prevDX);
} else {
dX = - stopPoint + dX;
if (backDX > 0) {
a += 0.01 * (cachedDX - prevDX);
}
dX /= a;
backDX = dX;
rightLock = false;
}
} else if (dX < 0) {
if (rightExpanded) {
if (backDX != 0) {
rightLock = true;
}
dX /= a;
prevA = a;
a -= swipeStopFraction * (cachedDX - prevDX);
} else {
dX = stopPoint + dX;
if (backDX < 0) {
a -= 0.01 * (cachedDX - prevDX);
}
dX /= a;
prevA = a;
backDX = dX;
leftLock = false;
}
}
prevDX = cachedDX;
if (leftLock) {
if (dX < stopPoint) {
dX = stopPoint;
}
}
if (rightLock) {
if (dX > -stopPoint) {
dX = -stopPoint;
}
}
// Below lines provide background for the empty area when the item is swiped.
// Container could be any layout that is the root layout of the item
final Paint paint = new Paint();
paint.setColor(getResources().getColor(R.color.colorAccent));
if (dX > 0) {
c.drawRect(holder.container.getLeft(), holder.container.getTop(), dX, holder.container.getBottom(), paint);
} else {
c.drawRect(holder.container.getRight() + dX, holder.container.getTop(), holder.container.getRight(), holder.container.getBottom(), paint);
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (prevHolder != null && viewHolder != null && prevHolder != viewHolder) {
leftLock = false;
rightLock = false;
leftExpanded = false;
rightExpanded = false;
prevHolder.container.setTranslationX(0); // Container could be any layout that is the root layout of the item
}
}
The control over what's going on in a recycler view is very limited and I couldn't push it any further. I hope this could be helpful.
Personally, I think doing this with ItemTouchHelper
is very hard to implement and manage and the result would not be completely perfect.