android - 具有自定义 Swipe 视图的 Android Recycler
问题描述
我正在尝试为 recyclerview 添加自定义滑动视图。以下代码运行良好。从这个链接得到。在此代码中,按钮是在 onDraw 方法中创建的。
enum ButtonsState {
GONE,
LEFT_VISIBLE,
RIGHT_VISIBLE
}
class SwipeController extends Callback {
private boolean swipeBack = false;
private ButtonsState buttonShowedState = ButtonsState.GONE;
private RectF buttonInstance = null;
private RecyclerView.ViewHolder currentItemViewHolder = null;
private SwipeControllerActions buttonsActions = null;
private static final float buttonWidth = 300;
public SwipeController(SwipeControllerActions buttonsActions) {
this.buttonsActions = buttonsActions;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(0, LEFT | RIGHT);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
if (swipeBack) {
swipeBack = buttonShowedState != ButtonsState.GONE;
return 0;
}
return super.convertToAbsoluteDirection(flags, layoutDirection);
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ACTION_STATE_SWIPE) {
if (buttonShowedState != ButtonsState.GONE) {
if (buttonShowedState == ButtonsState.LEFT_VISIBLE) dX = Math.max(dX, buttonWidth);
if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) dX = Math.min(dX, -buttonWidth);
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
else {
setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
if (buttonShowedState == ButtonsState.GONE) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
currentItemViewHolder = viewHolder;
}
private void setTouchListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
swipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
if (swipeBack) {
if (dX < -buttonWidth) buttonShowedState = ButtonsState.RIGHT_VISIBLE;
else if (dX > buttonWidth) buttonShowedState = ButtonsState.LEFT_VISIBLE;
if (buttonShowedState != ButtonsState.GONE) {
setTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
setItemsClickable(recyclerView, false);
}
}
return false;
}
});
}
private void setTouchDownListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
return false;
}
});
}
private void setTouchUpListener(final Canvas c, final RecyclerView recyclerView, final RecyclerView.ViewHolder viewHolder, final float dX, final float dY, final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
SwipeController.super.onChildDraw(c, recyclerView, viewHolder, 0F, dY, actionState, isCurrentlyActive);
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
setItemsClickable(recyclerView, true);
swipeBack = false;
if (buttonsActions != null && buttonInstance != null && buttonInstance.contains(event.getX(), event.getY())) {
if (buttonShowedState == ButtonsState.LEFT_VISIBLE) {
buttonsActions.onLeftClicked(viewHolder.getAdapterPosition());
}
else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) {
buttonsActions.onRightClicked(viewHolder.getAdapterPosition());
}
}
buttonShowedState = ButtonsState.GONE;
currentItemViewHolder = null;
}
return false;
}
});
}
private void setItemsClickable(RecyclerView recyclerView, boolean isClickable) {
for (int i = 0; i < recyclerView.getChildCount(); ++i) {
recyclerView.getChildAt(i).setClickable(isClickable);
}
}
private void drawButtons(Canvas c, RecyclerView.ViewHolder viewHolder) {
float buttonWidthWithoutPadding = buttonWidth - 20;
float corners = 16;
View itemView = viewHolder.itemView;
Paint p = new Paint();
RectF leftButton = new RectF(itemView.getLeft(), itemView.getTop(), itemView.getLeft() + buttonWidthWithoutPadding, itemView.getBottom());
p.setColor(Color.BLUE);
c.drawRoundRect(leftButton, corners, corners, p);
drawText("EDIT", c, leftButton, p);
RectF rightButton = new RectF(itemView.getRight() - buttonWidthWithoutPadding, itemView.getTop(), itemView.getRight(), itemView.getBottom());
p.setColor(Color.RED);
c.drawRoundRect(rightButton, corners, corners, p);
drawText("DELETE", c, rightButton, p);
buttonInstance = null;
if (buttonShowedState == ButtonsState.LEFT_VISIBLE) {
buttonInstance = leftButton;
}
else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) {
buttonInstance = rightButton;
}
}
private void drawText(String text, Canvas c, RectF button, Paint p) {
float textSize = 60;
p.setColor(Color.WHITE);
p.setAntiAlias(true);
p.setTextSize(textSize);
float textWidth = p.measureText(text);
c.drawText(text, button.centerX()-(textWidth/2), button.centerY()+(textSize/2), p);
}
public void onDrawing(Canvas c) {
if (currentItemViewHolder != null) {
drawButtons(c, currentItemViewHolder);
}
}
}
但我想将视图附加到滑动。所以我修改了如下代码。这段代码我可以向右滑动。但是,如果我再次单击或滑动,则滑动项目不会关闭。还有一些时间视图从recyclerview中出来。请帮助我。
public class SwipeTouchListener extends ItemTouchHelper.Callback {
private boolean swipeBack = false;
private boolean allowSwipeLeft, allowSwipeRight;
private List<Integer> unSwipablePosition = new ArrayList<>();
private ButtonsState buttonShowedState = ButtonsState.GONE;
private static final float buttonWidth = 300;
private SwipeControllerActions buttonsActions = null;
private RectF buttonInstance = null;
private View foregroundView = null;
public void setSwipeDirection(boolean allowSwipeLeft, boolean allowSwipeRight){
this.allowSwipeLeft = allowSwipeLeft;
this.allowSwipeRight = allowSwipeRight;
}
public void setUnSwipeableRows(Integer... rows) {
this.unSwipablePosition = new ArrayList<>(Arrays.asList(rows));
}
private int getSwipeFlags(){
if(allowSwipeRight && allowSwipeLeft)
return LEFT|RIGHT;
else if(allowSwipeLeft)
return LEFT;
else if(allowSwipeRight)
return RIGHT;
return 0;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(0, getSwipeFlags());
}
@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) {
}
@Override
public int convertToAbsoluteDirection(int flags, int layoutDirection) {
if (swipeBack) {
swipeBack = false;
return 0;
}
return super.convertToAbsoluteDirection(flags, layoutDirection);
}
@Override
public void onChildDraw(Canvas c,
RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
float dX, float dY,
int actionState, boolean isCurrentlyActive) {
int currentPosition = ((RecyclerAdapter.MyViewHolder) viewHolder).getAdapterPosition();
if(unSwipablePosition.contains(currentPosition))
return;
foregroundView = ((RecyclerAdapter.MyViewHolder) viewHolder).viewForeground;
if (actionState == ACTION_STATE_SWIPE) {
if (buttonShowedState != ButtonsState.GONE) {
if (buttonShowedState == ButtonsState.LEFT_VISIBLE) dX = Math.max(dX, buttonWidth);
if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) dX = Math.min(dX, -buttonWidth);
//Adding foreground layout
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
} else {
setTouchListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
if (buttonShowedState == ButtonsState.GONE) {
//Adding foreground layout
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
actionState, isCurrentlyActive);
}
}
private void setTouchListener(final Canvas c,
final RecyclerView recyclerView,
final RecyclerView.ViewHolder viewHolder,
final float dX, final float dY,
final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
swipeBack = event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP;
if (swipeBack) {
if (dX < -buttonWidth) buttonShowedState = ButtonsState.RIGHT_VISIBLE;
else if (dX > buttonWidth) buttonShowedState = ButtonsState.LEFT_VISIBLE;
if (buttonShowedState != ButtonsState.GONE) {
setTouchDownListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
setItemsClickable(recyclerView, false);
}
}
return false;
}
});
}
private void setTouchDownListener(final Canvas c,
final RecyclerView recyclerView,
final RecyclerView.ViewHolder viewHolder,
final float dX, final float dY,
final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
setTouchUpListener(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
return false;
}
});
}
private void setTouchUpListener(final Canvas c,
final RecyclerView recyclerView,
final RecyclerView.ViewHolder viewHolder,
final float dX, final float dY,
final int actionState, final boolean isCurrentlyActive) {
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
//Adding foreground layout
getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, 0F, dY,
actionState, false);
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
if (buttonsActions != null && buttonInstance != null && buttonInstance.contains(event.getX(), event.getY())) {
if (buttonShowedState == ButtonsState.LEFT_VISIBLE) {
buttonsActions.onLeftClicked(viewHolder.getAdapterPosition());
}
else if (buttonShowedState == ButtonsState.RIGHT_VISIBLE) {
buttonsActions.onRightClicked(viewHolder.getAdapterPosition());
}
}
setItemsClickable(recyclerView, true);
swipeBack = false;
buttonShowedState = ButtonsState.GONE;
}
return false;
}
});
}
解决方案
好像您正在尝试重新发明轮子 :) 请查看AndroidSwipeLayou并适应自己。我在我的大部分项目中都使用了它,效果很好。
推荐阅读
- sql - is there a way to group by using values in a range in sql
- linux - linux process TEXT segment expansion
- javascript - NODEJS : Front end and back end on different port?
- swift - SwiftUI Drag and Drop with a list
- ruby-on-rails - create a form object for has_many through association
- pandas - Accessing a csv file I made in databricks
- c++ - I'm not understanding how the Tower of Hanoi pegs are being updated properly
- python - 如何使用子图在 Seaborn 散点图中旋转 x 轴刻度标签
- node.js - Create a proxy server changin the referer
- simulation - OMNeT++ Returning an incomplete chunk