【问题标题】: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(),同时让它覆盖其他子元素?

【问题讨论】:

    标签: android parent-child android-view touch-event interception


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

    OnInterceptTouchEvent 的默认值为 false。

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

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

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

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

    public void requestDisallowInterceptTouchEvent (boolean 禁止拦截)

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

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

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

    1. 另一种方法是从 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;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多