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;
    }
});

参考链接

How to multi-select using drag gesture in RecyclerView ?

posted on 2024-07-09 18:49  明天有风吹  阅读(70)  评论(0编辑  收藏  举报

导航

+V atob('d2h5X251bGw=')

请备注:from博客园