首页 文章

具有子视图时,Android onInterceptTouchEvent不会获取操作

提问于
浏览
2

我有父视图v扩展线性布局 . 而且一个是LinearLayout,二个是ScrollView .

我想让MyParent View截取垂直 MotionEvent.ACTION_MOVE (仅向下方向,子 scrollview.getScrollY()==0 )并在父级处理它,其他MotionEvent由子进程处理

这是MyView.xml

<?xml version="1.0" encoding="utf-8"?>
    <merge xmlns:android="http://schemas.android.com/apk/res/android" >
    <LinearLayout
        android:id="@+id/child_linear>
        android:height="50dp"
        android:width="50dp">
            ...something...
    </LinearLayout>
    <ScrollView
       android:id="@+id/child_scrollview>
      <LinearLayout/>
      </LinearLayout>
    </ScrollView>
    </merge>

这是我的代码

public class MyCustomView extends LinearLayout{
    public MyCustomView(Context context) {
        super(context);
        init();
    }
    private void init(){
        setOrientation(LinearLayout.VERTICAL);
        LayoutInflater inflater = 
(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflater.inflate(R.layout.MyView, this, true);


    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        final int action = event.getAction();
        Log.d(log,"onInterceptTouchEvent : "+action);
        if (action == MotionEvent.ACTION_CANCEL || action == 
MotionEvent.ACTION_UP) {
            mIsBeingDragged = false;
            return false;
        }
        if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
            return true;
        }

        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                if (isReadyForPull()) {
                    final float y = event.getY(), x = 
event.getX();
                    final float diff, oppositeDiff, absDiff;
                    diff = y - mLastMotionY;
                    oppositeDiff = x - mLastMotionX;
                    absDiff = Math.abs(diff);
                    ViewConfiguration config = 
ViewConfiguration.get(getContext());

                    if (absDiff > config.getScaledTouchSlop() &&  
absDiff > Math.abs(oppositeDiff) && diff >= 1f) {
                            mLastMotionY = y;
                            mLastMotionX = x;
                            mIsBeingDragged = true;
                            Log.d(log,"Flag setting");
                    }
                }
                break;
            }
            case MotionEvent.ACTION_DOWN: {
                if (isReadyForPull()) {
                    mLastMotionY = mInitialMotionY = event.getY();
                    mLastMotionX = event.getX();
                    mIsBeingDragged = false;
                }
                break;
            }
        }
        return mIsBeingDragged;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        final int action=event.getAction();
        Log.d(log,"onTouchEvent : "+action);
        if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
            return false;
        }
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE: {
                Log.d(log,"ACTION MOVE RECEIVE");
                if (mIsBeingDragged) {
                    mLastMotionY = event.getY();
                    mLastMotionX = event.getX();
                    pullEvent();
                    return true;
                }
                break;
            }

            case MotionEvent.ACTION_DOWN: {
                if (isReadyForPull()) {
                    mLastMotionY = mInitialMotionY = event.getY();
                    return true;
                }
                break;
            }

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP: {
                if (mIsBeingDragged) {
                    mIsBeingDragged = false;

                    if (mState == State.RELEASE_TO_REFRESH ) {
                        setState(State.REFRESHING);
                        return true;
                    }
                    if (isRefreshing()) {
                        smoothScrollTo(-mHeaderHeight , null);
                        return true;
                    }

                    setState(State.RESET);
                    return true;
                }
                break;
            }
        }
        return false;
    }

isReadyForPull() 功能只检查这个

private boolean isReadyForPull(){
        return mScrollView.getScrollY()==0;
    }

我的代码效果很好 .

如果我向下移动我的子scrollview, onInterceptTouchEvent 首先得到 MotionEvent.ACTION_DOWN 并初始化值,然后按顺序 MotionEvent.ACTION_MOVEmIsBeingDragged 标志设置为 true 并返回 true . 所以我可以在ParentView的 onTouchEvent 中处理事件 .

否则,如果我向上移动子滚动视图,则 onInterceptTouchEvent 返回false并且我的子滚动视图获取MotionEvent并向下滚动 .

这是预期的工作 . 好 .

但是,当我触摸我的孩子线性布局时,它不起作用!

如果我触摸并拖动我的孩子linearlayout,我的CustomView的 onInterceptTouchEvent 得到 MotionEvent.ACTION_DOWN ,但无法得到第二个 MotionEvent.ACTION_MOVE .

为什么这不仅适用于儿童线性布局?

注意:我测试了几次,并且知道(默认)可触摸视图或视图组(如按钮,滚动视图等)可以使用我的代码,而(默认)不可触摸的视图或小部件(如imageview,framelayout)不适用于我的码 .

2 回答

  • 6

    该解决方案与this答案有关,因为它是标准 MotionEvent 处理的结果 .

    在我的代码中,当我触摸子 LinearLayout 时,父 CustomViewGroup 不拦截 MotionEvent ,因为它返回false,但是我的孩子 LinearLayout 也没有消耗 MotionEvent ,所以 MotionEvent 返回到父亲的 onTouchEvent ,而不是 onInterceptTouchEvent .

    另一方面,当我触摸我的孩子 ScrollView 时,无论是启用还是禁用滚动,它都会消耗 MotionEvent .

    ==> 我认为因为Android在原始版本被消耗或完成之前不再生成 MotionEvent ,所以父 CustomViewGroup 没有通过 onInterceptTouchEvent 获得 ACTION_MOVE MotionEvent ,当孩子不消耗时,它被管道传送到 onTouchEvent MotionEvent .

    我找到了两个解决方案

    解决方案一

    强行让我 LinearLayout 消耗 MotionEvent . 只有当孩子 LinearLayout 没有可触摸 ViewViewGroupWidget 时,此解决方案才可用 . 像这样:

    LinearLayout mLinearLayout = (LinearLayout)findViewById(R.id.child_linear);
    mLinearLayout.setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            return true;
        }
    });
    

    解决方案二

    CustomViewGrouponTouchEvent 中处理从孩子返回的 MotionEvent . 像这样:(如果 onTouchEvent 中的情况,只需添加其他内容) .

    case MotionEvent.ACTION_MOVE: {
        if (mIsBeingDragged) {
            mLastMotionY = event.getY();
            mLastMotionX = event.getX();
            pullEvent();
            return true;
        }
        else if (isReadyForPull()) {
            final float y = event.getY(), x = event.getX();
            final float diff, oppositeDiff, absDiff;
            diff = y - mLastMotionY;
            oppositeDiff = x - mLastMotionX;
            absDiff = Math.abs(diff);
            ViewConfiguration config = ViewConfiguration.get(getContext());
    
            if (absDiff > config.getScaledTouchSlop() &&  absDiff > 
            Math.abs(oppositeDiff) && diff >= 1f) {
                 mLastMotionY = y;
                 mLastMotionX = x;
                 mIsBeingDragged = true;
            }
         }
         break;
     }
    

    虽然解决方案1是某些情况下的快速解决方案,但解决方案2是最灵活和最可靠的 .

  • 0

    有很好的答案

    onInterceptTouchEvent only gets ACTION_DOWN

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
    MyLog.d(MyLog.DEBUG, "dispatchTouchEvent(): "+event.getAction());
    if (isEnabled()) {
        MyLog.d(MyLog.DEBUG, "dispatchTouchEvent()2: "+event.getAction());
    
        processEvent(event);//here you get all events include move & up
    
        super.dispatchTouchEvent(event);
    
        return true; //to keep receive event that follow down event
    }
    return super.dispatchTouchEvent(event);
    }
    

相关问题