如何在子视图和父视图组触摸事件之间变化

Posted

技术标签:

【中文标题】如何在子视图和父视图组触摸事件之间变化【英文标题】:How to vary between child and parent view group touch events 【发布时间】:2015-09-07 02:31:49 【问题描述】:

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

我会把评论贴在这里:

假设我只想覆盖触摸事件来处理一些 孩子们,我可以在这个函数中做什么来让它工作? 我的意思是,对于一些孩子来说,它会像往常一样工作,而对于一些孩子来说, parent-view 将决定他们是否会获得触摸事件。

所以问题是这样的:如何防止父onTouchEvent() 覆盖某些子元素的onTouchEvent(),同时让它覆盖其他子元素?

【问题讨论】:

【参考方案1】:
    嵌套视图组的onTouchEvents() 可以由boolean onInterceptTouchEvent 管理。

OnInterceptTouchEvent 的默认值为 false。

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

但是,在某些情况下,我们可能希望某些子元素管理OnTouchEvents,而某些子元素由父视图(或可能是父视图的父视图)管理。

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

    保护子元素免受父元素OnInterceptTouchEvent 影响的一种方法是实现requestDisallowInterceptTouchEvent。

public void requestDisallowInterceptTouchEvent (boolean 禁止拦截)

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

    如果OnInterceptTouchEvent 为假,则将评估子元素的OnTouchEvent。如果子元素中有处理各种触摸事件的方法,则任何禁用的相关事件处理程序都会将 OnTouchEvent 返回给父元素。

这个答案:https://***.com/a/13540006/3956566 很好地展示了触摸事件的传播是如何通过的:parent -> child|parent -> child|parent -> child views.

    另一种方法是从 OnInterceptTouchEvent 为父级返回不同的值。

这个例子取自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;

编辑:回答 cmets。 这是来自同一链接的一些代码,显示了如何在元素周围创建矩形的参数: 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);

【讨论】:

我不明白:您检查事件未传递给某些视图的部分在哪里?视图矩形内不应该有一些 x&y 坐标检查吗?我实际上问这个是因为我想为父视图设置一些手势(但在子视图中处理),并且点击仅适用于子视图。 我有一个父视图,里面有几个子视图。 On of the child-views 的特殊之处在于对父级的手势是在子视图上执行的。我想以这种方式保留它,除了单击事件,它不会在父视图上执行,而只会在子视图上执行。这就是我问的原因:我在哪里以及如何检查触摸的坐标,以避免在子视图之外单击。 还是不明白。我已经成功地通过在 childView 中创建一个“onTouch”函数(包括它的所有手势处理)来成功地做到这一点,它将通过其 onTouchListener 从父视图中调用,并且对于单击我已经做了 GestureDetectorCompat 检查是否事件在子视图中(在矩形上使用“getHitRect”和“contains”)。在活动中,我在子视图上使用了“setOnClickListener”,但也在其上使用了“setClickable(false)”,这样就不会干扰手势。 很抱歉,感谢您的帮助。在这里,为努力+1。 :) 好的,发布了一个更新的 repo,展示了我找到的问题和解决方案:github.com/androidDeveloperLB/MultiTouchPlaceholderView【参考方案2】:

让我们修改问题。

您碰巧有一个带有一群孩子的 ViewGroup。你想拦截这个 ViewGroup 的所有东西的触摸事件,除了一些孩子之外。

很长一段时间以来,我一直在寻找同一个问题的答案。没有设法找到任何合理的东西,因此我自己想出了以下解决方案。

以下代码 sn-p 提供了 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;

【讨论】:

以上是关于如何在子视图和父视图组触摸事件之间变化的主要内容,如果未能解决你的问题,请参考以下文章

UITapGestureRecognizer 在子视图中阻止 UIButton 的触摸事件

子 UIViewController 视图阻止容器 UIViewController 视图接收触摸事件

在 Android 中的多个视图上同时处理触摸事件

仅当触摸类型为手指时,如何将触摸事件传递给其他视图?

在重叠视图的情况下控制触摸事件传播

如何将触摸事件从一个视图传递到另一个视图