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

时间:2014-02-22 11:44:47

标签: java android eclipse layout view

我有父视图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;
    }

我的代码效果很好。

如果我向下移动子视图滚动视图,onInterceptTouchEvent首先获取 MotionEvent.ACTION_DOWN 并初始化值,然后按顺序 MotionEvent.ACTION_MOVE 设置 mIsBeingDragged 标记为 true 并返回 true 。所以我可以在 ParentView onTouchEvent

中处理事件

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

这是预期的工作。好。

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

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

为什么这不仅适用于儿童linearlayout?

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

2 个答案:

答案 0 :(得分:6)

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

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

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

==&gt; 我认为,因为Android在原始版本消费或完成之前不再生成MotionEvent,因此父CustomViewGroup不会通过ACTION_MOVE获取MotionEvent onInterceptTouchEvent,当孩子不使用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;
    }
});

解决方案二

处理MotionEvent CustomViewGroup中孩子返回的onTouchEvent。像这样:(如果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是最灵活和最可靠的。

答案 1 :(得分: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);
}