这是一个非常简短的问题: 最近谷歌已经更新了其材料设计指南,显示多项选择应该与Google-Photos应用程序(here)类似:
我注意到即使您已经处于多选模式,您仍然可以在任何地方使用此手势。
到目前为止我所做的是处理点击项目以进行多项选择,但我如何处理Google展示的内容?
答案 0 :(得分:2)
似乎它比我想象的要复杂,但是有一个库可供选择:
https://android-arsenal.com/details/1/5152
https://github.com/MFlisar/DragSelectRecyclerView
基于:
https://github.com/weidongjian/AndroidDragSelect-SimulateGooglePhoto
答案 1 :(得分:2)
尽管它很复杂并且有一些库可供使用,但是如果您可以为自己的目的开发一个库会更好(让我们想象一下,我们希望针对Boogle,Bookworm或Bejeweled等一种类型的游戏使用此功能...)。 / p>
因此,如果您想自己做,这里是一些入门的好技巧:
1)了解完全触摸事件(当向下,向上,向上,向下发送动作时;如何区分多点触摸和单点触摸)。
2)区别于onTouch和onIntercepTouch。
3)对RecyclerView.OnItemTouchListener进行一些研究,因为我们将利用它。
4)在研究时放置一些日志以了解流程事件,在学习触摸事件时不要调试。
接下来,您将开始做一个小例子:
abstract public class OnItemTouchMultiDragListener implements RecyclerView.OnItemTouchListener {
private static final int MAX_CLICK_DURATION = 200;
private boolean isIn; // Is touching still at the same item.
private String tagTouchableView; // View catches touch event in each item of recycler view.
private int countIn; // How many times touch event in item.
private long timeDown; // Time touch down on touchable view.
protected int startPos; // Position of item to start touching.
protected int lastPos; // Position of item last touching in.
protected int endPos; // Position of item to end touching (touch up).
private boolean isTouchDownAtTouchableView; // If touch down event is in a touchable view.
public OnItemTouchMultiDragListener(String tagTouchableView){
this.tagTouchableView = tagTouchableView;
}
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) {
if(motionEvent.getPointerCount() > 1){
// Touch with many fingers, don't handle.
return false;
}
int action = motionEvent.getAction();
if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP || action == MotionEvent.ACTION_CANCEL){
View v = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
RecyclerView.ViewHolder holder = null;
endPos = -1;
if(v != null) {
View vTouchable = v.findViewWithTag(tagTouchableView);
// If up in that item too, since we only in one item.
if (vTouchable != null && isInView(motionEvent, vTouchable)) {
holder = recyclerView.getChildViewHolder(v);
endPos = holder.getAdapterPosition();
}
}
// If touch down/ move only in one item.
if(countIn == 1 && isTouchDownAtTouchableView){
if(holder != null && endPos != -1) {
if (isPossiblyClick()) {
onClickUp(endPos, holder);
} else {
onLongClickUp(endPos, holder);
}
}
}else if (countIn > 1){
onDragMultiUp(lastPos, holder);
}else {
onUp();
}
// Reset touch status.
isIn = false;
isTouchDownAtTouchableView = false;
countIn = 0;
return false;
}
View v = recyclerView.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
if(v != null) {
View vTouchable = v.findViewWithTag(tagTouchableView);
if(vTouchable != null && isInView(motionEvent, vTouchable)) {
RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(v);
int pos = holder.getAdapterPosition();
if(isIn && pos == lastPos || pos == -1){
// Still in the same pos or can not determine what the pos is.
return false;
}
timeDown = Calendar.getInstance().getTimeInMillis();
isIn = true;
RecyclerView.ViewHolder holder = recyclerView.getChildViewHolder(v);
int pos = holder.getAdapterPosition();
if(action == MotionEvent.ACTION_DOWN){
onDownTouchableView(pos);
isTouchDownAtTouchableView = true;
}else if(isTouchDownAtTouchableView && action == MotionEvent.ACTION_MOVE){
onMoveTouchableView(pos);
}
onInItemAndInTouchableView(motionEvent, pos);
if(countIn == 0){
startPos = pos;
}
lastPos = pos;
countIn ++;
}else {
isIn = false;
onInItemButNotInTouchable();
}
}else {
isIn = false;
onOutItem();
}
return false;
}
@Override
public void onTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent motionEvent) { }
@Override
public void onRequestDisallowInterceptTouchEvent(boolean b) { }
private boolean isPossiblyClick() {
long clickDuration = Calendar.getInstance().getTimeInMillis() - timeDown;
return clickDuration < MAX_CLICK_DURATION;
}
private boolean isInView(MotionEvent ev, View... views) {
Rect rect = new Rect();
for (View v : views) {
v.getGlobalVisibleRect(rect);
Log.d("","");
if (rect.contains((int) ev.getRawX(), (int) ev.getRawY()))
return true;
}
return false;
}
public void onInItemAndInTouchableView(MotionEvent motionEvent, int pos){}
abstract public void onDownTouchableView(int pos);
abstract public void onMoveTouchableView(int pos);
public void onUp(){}
public void onClickUp(int pos, RecyclerView.ViewHolder holder){}
public void onLongClickUp(int pos, RecyclerView.ViewHolder holder){}
public void onDragMultiUp(int endPos, RecyclerView.ViewHolder holder){}
public void onInItemButNotInTouchable(){}
public void onOutItem(){}
public int getStartPos() {
return startPos;
}
public int getEndPos() {
return endPos;
}
}
要使用它,请执行以下操作:
private OnItemTouchMultiDragListener onItemTouchMultiDragListener = new OnItemTouchMultiDragListener("touchable") {
@Override
public void onDownTouchableView(int pos) {
// Write log here.
// This is an abstract method, you must implement.
}
@Override
public void onMoveTouchableView(int pos) {
// Write log here.
// This is an abstract method, you must implement.
}
};
您可以覆盖的其他方法包括:onInItemAndInTouchableView
,onUp
,onClickUp
,onLongClickUp
,onDragMultiUp
,onInItemButNotInTouchable
,{{ 1}}。顺便说一句,在测试时写这些方法的日志,以了解何时调用。
当然,在实例化onOutItem
的实例之后,我们必须将其添加到回收器视图中,例如:
OnItemTouchListener
您还需要注意的一件事是,在回收站视图中项目的布局,这是我的,它是网格布局中的正方形项目,周围填充着彼此分开的项目(因此,仅项目视图的一部分接受触摸事件,而不是填充区域):
recyclerView.addOnItemTouchListener(onItemTouchMultiDragListener);
--------------编辑----------------
下面是一个简单的示例:https://github.com/mttdat/example-multi-drag-recycleview