如何在子视图组和父视图组触摸事件之间进行更改

时间:2015-06-21 15:54:07

标签: android parent-child android-view ontouchevent interception

我决定将这个问题发布回答to this comment回答这个问题:
 How to handle click in the child Views, and touch in the parent ViewGroups?

我会在此处粘贴评论:

  

假设我想覆盖触摸事件仅用于处理某些事件   孩子们,我可以在这个功能中做些什么让它起作用?   我的意思是,对于一些孩子来说,它会照常工作,而对于一些孩子来说   父母视图将决定他们是否会获得触摸事件。

所以问题是:如何阻止父onTouchEvent()覆盖某些子元素onTouchEvent(),同时覆盖其他子元素?

2 个答案:

答案 0 :(得分:28)

  1. 嵌套视图组的onTouchEvents()可由boolean onInterceptTouchEvent管理。
  2. OnInterceptTouchEvent的默认值为false。

    在孩子面前收到父母的onTouchEvent。如果OnInterceptTouchEvent返回false,它会将链中的运动事件发送到子项的OnTouchEvent处理程序。如果它返回true,则父级将处理触摸事件。

    然而,可能存在这样的情况:我们希望一些子元素管理OnTouchEvent,一些子元素由父视图(或可能是父视图的父元素)管理。

    这可以通过多种方式进行管理。

    1. 通过实施requestDisallowInterceptTouchEvent,可以保护子元素免受父OnInterceptTouchEvent影响的一种方式。
    2.   

      public void requestDisallowInterceptTouchEvent(boolean   disallowIntercept)

      如果元素启用了事件处理程序,这可以防止任何父视图管理此元素的OnTouchEvent

      1. 如果OnInterceptTouchEvent为false,则会评估子元素OnTouchEvent。如果处理各种触摸事件的子元素中有方法,则禁用的任何相关事件处理程序都会将OnTouchEvent返回给父元素。
      2. 这个回答:
        https://stackoverflow.com/a/13540006/3956566可以很好地显示触摸事件的传播如何通过:
         parent -> child|parent -> child|parent -> child views.

        1. 另一种方法是从父级的OnInterceptTouchEvent返回不同的值。
        2. 此示例取自Managing Touch Events in a ViewGroup,演示了当用户滚动时如何拦截孩子的OnTouchEvent

          4A。

          @Override
          public boolean onInterceptTouchEvent(MotionEvent ev) {
              /*
               * This method JUST determines whether we want to intercept the motion.
               * If we return true, onTouchEvent will be called and we do the actual
               * scrolling there.
               */
          
          
              final int action = MotionEventCompat.getActionMasked(ev);
          
              // Always handle the case of the touch gesture being complete.
              if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
                  // Release the scroll.
                  mIsScrolling = false;
                  return false; // Do not intercept touch event, let the child handle it
              }
          
              switch (action) {
                  case MotionEvent.ACTION_MOVE: {
                      if (mIsScrolling) {
                          // We're currently scrolling, so yes, intercept the 
                          // touch event!
                          return true;
                      }
          
                      // If the user has dragged her finger horizontally more than 
                      // the touch slop, start the scroll
          
                      // left as an exercise for the reader
                      final int xDiff = calculateDistanceX(ev); 
          
                      // Touch slop should be calculated using ViewConfiguration 
                      // constants.
                      if (xDiff > mTouchSlop) { 
                          // Start scrolling!
                          mIsScrolling = true;
                          return true;
                      }
                      break;
                  }
                  ...
              }
          
              // In general, we don't want to intercept touch events. They should be 
              // handled by the child view.
              return false;
          }
          

          编辑:回答评论 这是来自同一链接的一些代码,显示如何在元素周围创建矩形的参数:
          4B。

          // The hit rectangle for the ImageButton
          myButton.getHitRect(delegateArea);
          
          // Extend the touch area of the ImageButton beyond its bounds
          // on the right and bottom.
          delegateArea.right += 100;
          delegateArea.bottom += 100;
          
          // Instantiate a TouchDelegate.
          // "delegateArea" is the bounds in local coordinates of 
          // the containing view to be mapped to the delegate view.
          // "myButton" is the child view that should receive motion
          // events.
          TouchDelegate touchDelegate = new TouchDelegate(delegateArea, myButton);
          
          // Sets the TouchDelegate on the parent view, such that touches 
          // within the touch delegate bounds are routed to the child.
          if (View.class.isInstance(myButton.getParent())) {
              ((View) myButton.getParent()).setTouchDelegate(touchDelegate);
          }
          

答案 1 :(得分:1)

让我们重新解决这个问题。

你碰巧有一群带有一堆孩子的ViewGroup。您希望拦截触摸事件以获取具有此ViewGroup的所有内容,但有一些孩子的例外情况。

我一直在寻找同一个问题的答案。没有找到任何合理的东西,因此我自己提出以下解决方案。

以下代码段概述了ViewGroup的相关代码,它拦截了所有触摸,但来自恰好具有特殊标记集的视图的代码除外(您应该在代码中的其他位置设置它)。

private static int NO_INTERCEPTION;

private boolean isWithinBounds(View view, MotionEvent ev) {
    int xPoint = Math.round(ev.getRawX());
    int yPoint = Math.round(ev.getRawY());
    int[] l = new int[2];
    view.getLocationOnScreen(l);
    int x = l[0];
    int y = l[1];
    int w = view.getWidth();
    int h = view.getHeight();
    return !(xPoint < x || xPoint > x + w || yPoint < y || yPoint > y + h);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    for (int i=0; i<floatingMenuItems.getChildCount(); i++){
        View child = floatingMenuItems.getChildAt(i);
        if (child == null || child.getTag(NO_INTERCEPTION) == null) {
            continue;
        }
        if(isWithinBounds(child, ev)){
            return false;
        }
    }
    return true;
}