onInterceptTouchEvent、onTouchEvent只能看到ACTION_DOWN动作

7
我有一个顶层ViewGroup,我称之为SliderView,在其中我想检测滑动。这在大多数情况下都有效,但仍存在一种奇怪的故障。
SliderView的本质是重写onInterceptTouchEvent方法,并且一旦用户实际滑动,返回“true”以防止其他视图看到MotionEvent。以下是代码片段:
public class SliderView extends ViewGroup
{
  enum MoveState { MS_NONE, MS_HSCROLL, MS_VSCROLL };
  private MoveState moveState = MoveState.MS_NONE;

  ... other code ...

  public boolean onInterceptTouchEvent(MotionEvent e)
  {
    final int action = e.getAction();
    switch (action & MotionEvent.ACTION_MASK)
    {
      case MotionEvent.ACTION_DOWN:
        moveState = MoveState.MS_NONE;
        break;

      case MotionEvent.ACTION_MOVE:
        if (moveState == MoveState.MS_NONE)
        {
          if (motion is horizontal)
          {
            moveState = MoveState.MS_VSCROLL;
            return true;
          }
          else
            moveState = MoveState.MS_VSCROLL; // let child window handl MotionEvent
        }
        else if (moveState == MoveState.MS_HSCROLL)
          return true; // don't let children see motion event.
    }
    return super.onInterceptTouchEvent (e);
  }

  ... other code ...
}

我的理解是,我的SliderView(最外层视图)应该始终接收onInterceptTouchEvent。在我的一个测试中,顶层子项是一个ScrollView时,onInterceptTouchEvent会得到ACTION_MOVE并且我的代码会按预期执行。但在另一种情况下,顶层子项是LinearLayout时,有时会失败:它总是会得到ACTION_DOWN,但只有当用户触摸LinearLayout内的小部件时才会得到ACTION_MOVE;如果触摸空白区域,则只会传递ACTION_DOWN。
我注意到它的行为就好像失败的情况触摸发生在SliderView之外。但是,如果是这样,为什么我会收到ACTION_DOWN事件呢?
第二个注意事项:查看ScrollView的源代码,我看到它检查了“inChild”;我还没有弄清楚那是做什么的,以及它可能如何相关。

不,但是在阅读更多文档时,我了解到有一种叫做ViewPager的东西,它可以实现水平滑动。由于我正在完善其他内容,所以还没有尝试过。我不知道你想要做什么,但如果你尝试了ViewPager,请告诉我结果。 - Peri Hartman
4个回答

6

由于用户user123321在这里的回答

只有当父视图的子视图从onTouchEvent返回“true”时,onInterceptTouchEvent才会被调用。一旦子视图返回true,父视图现在有机会拦截该事件。


3
你只需要调用以下代码:
requestDisallowInterceptTouchEvent(true);

在父视图上,就像这样 -
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {
            view.getParent().requestDisallowInterceptTouchEvent(true);
            switch(motionEvent.getActio){
            }

            return false; 

         }

1
这可能是一个正确的答案,但由于问题非常古老 - 而我的代码已经演变成了其他东西 - 我将无法验证。 - Peri Hartman
抱歉,我不理解你的问题。 - Peri Hartman
如果我应用这个解决方案,滚动将停止工作。 - Prince Dholakiya

1

实际上,它仅对两种情况返回“true”。其他情况返回super.onInterceptTouchEvent()。 - Peri Hartman

0

当拦截 onTouchEvent 时,有两件事情要做才能正确地拦截触摸(其他所有内容都是默认的)。

  1. 在onInterceptTouchEvent()中返回false

    @Override
    public boolean onInterceptTouchEvent(MotionEvent me) {
        return false;
    }
    
  2. 在onTouchEvent()中返回true

    @Override
    public boolean onTouchEvent(MotionEvent me) {
    
        switch (me.getAction()) {
        case MotionEvent.ACTION_DOWN:
            log("MotionEvent.ACTION_DONE");
            break;
        case MotionEvent.ACTION_MOVE:
            log("MotionEvent.ACTION_MOVE");
            break;
        case MotionEvent.ACTION_CANCEL:
            log("MotionEvent.ACTION_CANCEL");
            userActionDown = false;
            break;
        case MotionEvent.ACTION_UP:
            log("MotionEvent.ACTION_UP");
            break;
        }
    
        return true;
    }
    
  3. 然后,对于您的情况(以及其他情况),请像上面所示,在onTouchEvent()中进行所有计算。 onInterceptTouchEvent()仅会在ACTION_DOWN时调用一次。但是,onTouchEvent也将获得ACTION_DOWN事件,并且您需要在那里返回true,而不是super。

关于 onInterceptTouchEvent() 的更多信息:http://developer.android.com/reference/android/view/ViewGroup.html#onInterceptTouchEvent(android.view.MotionEvent)

附言 - 当你在这里提问时,你应该写下你想要做什么的描述。你可能会发现更好的解决方法。对于你的导航问题,你真正寻找的答案是 ViewPager。它非常好用且易于实现。你还应该查看一些其他易于开发者使用的 Android 导航模式:link


我在2012年11月发现了ViewPager,并在上面进行了说明。 - Peri Hartman
是的,这就是我评论它的原因。你说:“我还没有尝试过,因为我现在正在完善其他东西。我不知道你想做什么,但如果你尝试ViewPager,请让我知道结果。”所以,我告诉你结果,因为我已经使用了ViewPager。;) 还有其他问题吗? - Anonsage
1
是的,我尝试过它,而且它运行得非常好。请参见https://dev59.com/T2Yr5IYBdhLWcg3wdp4B - Peri Hartman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接