Java 从RecyclerView中的项触发单击并拖动操作

Java 从RecyclerView中的项触发单击并拖动操作,java,android,android-recyclerview,Java,Android,Android Recyclerview,我有一个垂直滚动的RecyclerView,我想允许点击上面的项目并重新排序 RecyclerView中的每个项目在左侧都有一个“StatusIndicator”(从AppCompatImageButton继承)。我已经在StatusIndicator中添加了一个手势检测器,除了垂直拖动没有被发送到StatusIndicator之外,这似乎是可行的 我看到的行为是,如果我尝试在StatusIndicator上垂直滚动(打算触发拖动),则不会触发拖动,也不会滚动RecyclerView。如果我继续

我有一个垂直滚动的RecyclerView,我想允许点击上面的项目并重新排序

RecyclerView中的每个项目在左侧都有一个“StatusIndicator”(从AppCompatImageButton继承)。我已经在StatusIndicator中添加了一个手势检测器,除了垂直拖动没有被发送到StatusIndicator之外,这似乎是可行的

我看到的行为是,如果我尝试在StatusIndicator上垂直滚动(打算触发拖动),则不会触发拖动,也不会滚动RecyclerView。如果我继续滚动手势,但突然水平滚动,我可以拖动项目

我假设ACTION_DOWN事件到达StatusIndicator的GestureDetector以触发拖动,但RecyclerView捕获以下(主要是垂直)ACTION_MOVE(假设它需要处理垂直滚动)。一旦手势涉及更多的水平移动而非垂直移动,RecyclerView::onInterceptTouchEvent将不再捕获事件(在调试中确认),然后动作将按预期发送到状态指示器

这里的正确方法是什么


StatusIndicator.java的相关位:

class StatusIndicator extends AppCompatImageButton {
    class GestureTouch extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onDown(MotionEvent event) {
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            if (mStatusIndicatorListener != null)
                mStatusIndicatorListener.incrementStatus();
            createDrawableState();
            return true;
        }

        @Override
        public boolean onScroll (MotionEvent e1,
                                 MotionEvent e2,
                                 float distanceX,
                                 float distanceY) {
            mStatusIndicatorListener.startDrag();
            return true;
        }
    }

    private StatusIndicatorListener mStatusIndicatorListener;
    private GestureDetector mTouchDetector;

    public StatusIndicator(Context context, AttributeSet attrs) {
        super(context, attrs);
        mTouchDetector = new GestureDetector(context,new GestureTouch());
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mTouchDetector.onTouchEvent(event);
        return true;
    }

我相信发生的事情是,任何动作移动事件都会进入堆栈,并被RecyclerView在其onInterceptTouchEvent方法中捕获。在此RecyclerView中,LayoutManager.CanScrollVertical()用于确定RecyclerView应捕获事件

最后,我继承了LinearLayoutManager并重写了canScrollVertically(),因此可以通过调用新方法手动阻止它。然后,在RecyclerView.onInterceptTouchEvent中,我检查了传入的ACTION_DOWN事件是否超过了StatusIndicator,如果是,则强制RecyclerView.LayoutManater.CanScrollVertical()返回false,直到下一个ACTION_DOWN事件

这是一个相当粗糙的解决方案,但看起来很健壮。如果有人提出一个“合适的”解决方案,我会很乐意看到的


新的布局管理器:

public class ListLayoutManager extends LinearLayoutManager {
    boolean mScrollBlocked;

    public ListLayoutManager(Context context) {
        super(context);
        mScrollBlocked = false;
    }

    @Override
    public boolean canScrollVertically() {
        return (!mScrollBlocked && super.canScrollVertically());
    }

    public void blockScrolling() {
        mScrollBlocked = true;
    }
    public void unblockScrolling() {
        mScrollBlocked = false;
    }
}
已覆盖onInterceptTouchEvent的扩展回收视图:

    ListLayoutManager mListLayoutManager;

    public ListRecyclerView(Context context) {
        super(context);
    }
    public ListRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public ListRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    // We override touch handling so the RecyclerView doesn't handle
    // anything which occurs over the StatusIndicator.
    @Override
    public boolean onInterceptTouchEvent(MotionEvent e) {
        if (e.getActionMasked() == MotionEvent.ACTION_DOWN) {
            mListLayoutManager.unblockScrolling();

            View child = findChildViewUnder(e.getX(), e.getY());
            if (child != null) {
                StatusIndicator statusInd = child.findViewById(R.id.rowStatus);
                if ((statusInd != null) &&
                    (e.getX() <= statusInd.getWidth())) {
                    mListLayoutManager.blockScrolling();
                }
            }
        }
        return super.onInterceptTouchEvent(e);
    }

    public void setListLayoutManager(ListLayoutManager lm) {
        mListLayoutManager = lm;
        setLayoutManager(mListLayoutManager);
    }
}
ListLayoutManager-mListLayoutManager;
公共ListRecyclerView(上下文){
超级(上下文);
}
公共ListRecyclerView(上下文、属性集属性){
超级(上下文,attrs);
}
公共ListRecyclerView(上下文、属性集属性、int-defStyleAttr){
super(上下文、attrs、defStyleAttr);
}
//我们覆盖触摸处理,因此RecyclerView无法处理
//状态指示器上发生的任何事件。
@凌驾
公共布尔值onInterceptTouchEvent(运动事件e){
如果(例如getActionMasked()==MotionEvent.ACTION\u DOWN){
mListLayoutManager.unbocrolling();
View child=findChildViewUnder(e.getX(),e.getY());
if(child!=null){
StatusIndicator statusInd=child.findViewById(R.id.rowStatus);
if((statusInd!=null)&&
(e.getX()