Reputation: 1
As the title suggests, I'm trying to make a RecyclerView that reveals a button when swiped to the left, while making it so that the content will remain visible when it is swiped. I managed to do so by making it's LinearLayout with id: content move to the right whenever the button is revealed as shown here. However, when I try to click another item, the button hides correctly, but the previous content does not return to its original position as shown here. Also, the button icon does not hide completely when I click the same item, while the content returns to its original position normally as shown here.
ManageUsersActivity.java:
new UserSwipeHelper(this, recyclerView, 250) {
@Override
public void instantiateUserButton(RecyclerView.ViewHolder viewHolder, List<UserSwipeHelper.UserButton> buffer) {
buffer.add(new UserButton(ManageUsersActivity.this,
"",
R.drawable.ic_delete,
0,
Color.parseColor("#FF3C30"),
pos -> Toast.makeText(ManageUsersActivity.this, "Remove Click", Toast.LENGTH_SHORT).show()));
}
};
UserSwipeHelper.java:
public abstract class UserSwipeHelper extends ItemTouchHelper.SimpleCallback {
int buttonWidth;
private final RecyclerView recyclerView;
private List<UserButton> userButtonList;
private final GestureDetector gestureDetector;
private int swipePosition = -1;
private float swipeThreshold = 0.5f;
private final Map<Integer, List<UserButton>> buttonBuffer;
private final Queue<Integer> removerQueue;
@SuppressLint("ClickableViewAccessibility")
public UserSwipeHelper(Context context, RecyclerView recyclerView, int buttonWidth) {
super(0, ItemTouchHelper.LEFT);
this.recyclerView = recyclerView;
this.userButtonList = new ArrayList<>();
GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {
@Override
public boolean onSingleTapUp(@NonNull MotionEvent e) {
for (UserButton button : userButtonList) {
if (button.onClick(e.getX(), e.getY())) {
break;
}
}
return super.onSingleTapUp(e);
}
};
this.gestureDetector = new GestureDetector(context, gestureListener);
View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent e) {
if (swipePosition < 0) return false;
Point point = new Point((int) e.getRawX(), (int) e.getRawY());
RecyclerView.ViewHolder swipeViewHolder = recyclerView.findViewHolderForAdapterPosition(swipePosition);
assert swipeViewHolder != null;
View swipedItem = swipeViewHolder.itemView;
Rect rect = new Rect();
swipedItem.getGlobalVisibleRect(rect);
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y) {
gestureDetector.onTouchEvent(e);
} else {
removerQueue.add(swipePosition);
swipePosition = -1;
recoverSwipedItem();
}
}
return false;
}
};
this.recyclerView.setOnTouchListener(onTouchListener);
this.buttonBuffer = new HashMap<>();
this.buttonWidth = buttonWidth;
removerQueue = new LinkedList<>() {
@Override
public boolean add(Integer integer) {
if (contains(integer)) {
return false;
} else {
return super.add(integer);
}
}
};
attachSwipe();
}
private void attachSwipe() {
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
private synchronized void recoverSwipedItem() {
while (!removerQueue.isEmpty()) {
int pos = removerQueue.poll();
if (pos > -1) {
Objects.requireNonNull(recyclerView.getAdapter()).notifyItemChanged(pos);
}
}
}
private Bitmap drawableToBitmap(Drawable d) {
if(d instanceof BitmapDrawable) {
return ((BitmapDrawable)d).getBitmap();
}
Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
return bitmap;
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) { return false; }
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
if (swipePosition != pos) {
removerQueue.add(swipePosition);
}
swipePosition = pos;
if (buttonBuffer.containsKey(swipePosition)) {
userButtonList = buttonBuffer.get(swipePosition);
} else {
userButtonList.clear();
}
buttonBuffer.clear();
swipeThreshold = 0.5f * userButtonList.size() * buttonWidth;
recoverSwipedItem();
}
@Override
public float getMoveThreshold(@NonNull RecyclerView.ViewHolder viewHolder) { return swipeThreshold; }
@Override
public float getSwipeEscapeVelocity(float defaultValue) { return 0.1f * defaultValue; }
@Override
public float getSwipeVelocityThreshold(float defaultValue) { return 5.0f * defaultValue; }
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
int pos = viewHolder.getAdapterPosition();
float translationX = dX;
View itemView = viewHolder.itemView;
if (pos < 0) {
swipePosition = pos;
return;
}
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if (dX < 0) {
List<UserButton> buffer = new ArrayList<>();
if (!buttonBuffer.containsKey(pos)) {
instantiateUserButton(viewHolder, buffer);
buttonBuffer.put(pos, buffer);
}
else {
buffer = buttonBuffer.get(pos);
}
translationX = dX * Objects.requireNonNull(buffer).size() * buttonWidth / itemView.getWidth();
// Move the layout to the right
itemView.findViewById(R.id.content).setTranslationX(-translationX);
drawButton(c, itemView, buffer, pos, translationX);
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
private void drawButton(Canvas c, View itemView, List<UserButton> buffer, int pos, float translationX) {
float right = itemView.getRight();
float dButtonWidth = -1 * translationX / buffer.size();
for (UserButton button : buffer) {
float left = right - dButtonWidth;
button.onDraw(c, new RectF(left, itemView.getTop(), right, itemView.getBottom()), pos);
right = left;
}
}
public abstract void instantiateUserButton(RecyclerView.ViewHolder viewHolder, List<UserButton> buffer);
public class UserButton {
private final String text;
private final int imageResId;
private final int textSize;
private final int color;
private final UserButtonClickListener listener;
private RectF clickRegion;
private final Context context;
private int pos;
public UserButton(Context context, String text, int imageResId, int textSize, int color, UserButtonClickListener listener) {
this.text = text;
this.imageResId = imageResId;
this.textSize = textSize;
this.color = color;
this.listener = listener;
this.context = context;
}
public boolean onClick(float x, float y) {
if (clickRegion != null && clickRegion.contains(x,y)) {
listener.onClick(pos);
return true;
}
return false;
}
public void onDraw(Canvas c, RectF rectF, int pos) {
Paint p = new Paint();
p.setColor(color);
c.drawRect(rectF, p);
p.setColor(Color.WHITE);
p.setTextSize(textSize);
Rect r = new Rect();
float cHeight = rectF.height();
float cWidth = rectF.width();
p.setTextAlign(Paint.Align.LEFT);
p.getTextBounds(text, 0, text.length(), r);
float x, y;
if (imageResId == 0) {
x = cWidth / 2f - r.width() / 2f - r.left;
y = cHeight / 2f + r.height() / 2f - r.bottom;
c.drawText(text, rectF.left + x, rectF.top + y, p);
} else {
@SuppressLint("UseCompatLoadingForDrawables") Drawable d = context.getDrawable(imageResId);
if (d == null) return;
Bitmap bitmap = drawableToBitmap(d);
// Calculate the center position for the bitmap
x = rectF.left + rectF.width() / 2f; // Center of the button
y = rectF.top + rectF.height() / 2f; // Center of the button
// Adjust the position to account for the bitmap's size
x -= bitmap.getWidth() / 2f; // Move left by half the bitmap's width
y -= bitmap.getHeight() / 2f; // Move up by half the bitmap's height
c.drawBitmap(bitmap, x, y, p);
}
clickRegion = rectF;
this.pos = pos;
}
}
}
Upvotes: 0
Views: 12
Reputation: 1
So, I managed to solve the icon not completely hiding by making the following changes:
Original Code:
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y) {
gestureDetector.onTouchEvent(e);
} else {
removerQueue.add(swipePosition);
swipePosition = -1;
recoverSwipedItem();
}
}
New Code:
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP || e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y && rect.right < point.x) {
gestureDetector.onTouchEvent(e);
} else {
removerQueue.add(swipePosition);
swipePosition = -1;
recoverSwipedItem();
}
}
However, I still haven't found a fix for the content moving to the right.
Upvotes: 0