android RecyclerView 长按之后滑动手指多选
关键函数与类
LinearLayoutManager.canScrollHorizontally()
LinearLayoutManager.canScrollVertically()
RecyclerView.OnItemTouchListener
核心代码
OnMultiItemSelectListener.java:
import android.graphics.Color;
import android.graphics.Rect;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class OnMultiItemSelectListener implements RecyclerView.OnItemTouchListener {
static final String TAG = "OnMultiItemSelectListener";
boolean is_down = false;
boolean is_canceled = false;
long ts_down = 0;
int down_position = 0;
int last_position = 0;
boolean canScroll = true;
float down_raw_x = 0;
float down_raw_y = 0;
// 一定要 return false !!! 如果在中间的 move 事件 return true 会导致后续的 up 事件不再进到这个函数,所以全程 return false 即可 !!!
@Override
public boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
// Touch with many fingers, only handle the first finger
if (e.getActionIndex() > 0) return false;
switch (e.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
if (is_down) return false;
View viewUnder = rv.findChildViewUnder(e.getX(), e.getY());
if (viewUnder == null) return false;
RecyclerView.ViewHolder viewHolder = rv.findContainingViewHolder(viewUnder);
if (viewHolder == null) return false;
Rect rect = new Rect();
if (!viewHolder.itemView.getGlobalVisibleRect(rect) || !rect.contains((int)e.getRawX(), (int)e.getRawY()))
return false;
is_down = true;
is_canceled = false;
down_position = viewHolder.getAdapterPosition();
last_position = down_position;
down_raw_x = e.getRawX();
down_raw_y = e.getRawY();
ts_down = e.getDownTime();
long ts_function = ts_down;
rv.postDelayed(()->{
if (!is_down || is_canceled || ts_function != ts_down) return;
canScroll = false;
onCanScrollChange(false);
onAdapterPositionSelect(rv, down_position);
}, 1000);
return false;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_CANCEL:
if (!canScroll) {
onSelectFinished();
}
onCanScrollChange(true);
is_down = false;
canScroll = true;
break;
case MotionEvent.ACTION_MOVE:
float diffX = Math.abs(down_raw_x - e.getRawX());
float diffY = Math.abs(down_raw_y - e.getRawY());
if (e.getEventTime() - e.getDownTime() < 900 && (diffX > 10 || diffY > 10)){
is_canceled = true;
onCanScrollChange(true);
canScroll = true;
}
onTouchEvent(rv, e);
default:
break;
}
return false;
}
@Override
public void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e) {
if (canScroll || e.getActionMasked() != MotionEvent.ACTION_MOVE)
return;
View viewUnder = rv.findChildViewUnder(e.getX(), e.getY());
if (viewUnder == null) return;
RecyclerView.ViewHolder viewHolder = rv.findContainingViewHolder(viewUnder);
if (viewHolder == null) return;
Rect rect = new Rect();
if (!viewHolder.itemView.getGlobalVisibleRect(rect) || !rect.contains((int)e.getRawX(), (int)e.getRawY()))
return;
int position = viewHolder.getAdapterPosition();
if (position == last_position) return;
// 向原来的位置滑动了
if (Math.abs(down_position - position) < Math.abs(down_position - last_position)) {
onAdapterPositionUnSelect(rv, last_position);
} else {
onAdapterPositionSelect(rv, position);
}
last_position = position;
if (rv.getLayoutManager() instanceof LinearLayoutManager) {
LinearLayoutManager layoutManager = (LinearLayoutManager)rv.getLayoutManager();
if (layoutManager == null) return;
if (position == layoutManager.findFirstVisibleItemPosition() && position > 0) {
rv.smoothScrollToPosition(position - 1);
}
else if (position == layoutManager.findLastVisibleItemPosition() && rv.getAdapter() != null && position < rv.getAdapter().getItemCount()-1) {
rv.smoothScrollToPosition(position + 1);
}
}
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
public void onAdapterPositionSelect(@NonNull RecyclerView rv, int pos) {
RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(pos);
if (vh == null) return;
vh.itemView.setBackgroundColor(Color.CYAN);
}
public void onAdapterPositionUnSelect(@NonNull RecyclerView rv, int pos) {
RecyclerView.ViewHolder vh = rv.findViewHolderForAdapterPosition(pos);
if (vh == null) return;
vh.itemView.setBackgroundColor(Color.WHITE);
}
public void onSelectFinished() {}
public void onCanScrollChange(boolean canScroll){}
}
MainActivity.java:
// ...
mCanRecyclerViewScroll = true;
recyclerView.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false) {
@Override
public boolean canScrollHorizontally() {
return mCanRecyclerViewScroll && super.canScrollHorizontally();
}
@Override
public boolean canScrollVertically() {
return mCanRecyclerViewScroll && super.canScrollVertically();
}
});
recyclerView.addOnItemTouchListener(new OnMultiItemSelectListener(){
@Override
public void onAdapterPositionSelect(@NonNull RecyclerView rv, int pos) {
if (pos < 0 || pos >= mMyItemArrayList.size()) return;
MyItem item = mMyItemArrayList.get(pos);
item.isMultiSelected = !item.isMultiSelected;
if(rv.getAdapter() != null)rv.getAdapter().notifyItemChanged(pos);
}
@Override
public void onAdapterPositionUnSelect(@NonNull RecyclerView rv, int pos) {
if (pos < 0 || pos >= mMyItemArrayList.size()) return;
MyItem item = mMyItemArrayList.get(pos);
item.isMultiSelected = !item.isMultiSelected;
if(rv.getAdapter() != null)rv.getAdapter().notifyItemChanged(pos);
}
@Override
public void onSelectFinished() {
}
@Override
public void onCanScrollChange(boolean canScroll) {
mCanItemListRecyclerViewScroll = canScroll;
}
});
参考链接
+V why_null 请备注:from博客园