如何在子视图和父视图组触摸事件之间变化
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,则父级将处理触摸事件。
但是,在某些情况下,我们可能希望某些子元素管理OnTouchEvent
s,而某些子元素由父视图(或可能是父视图的父视图)管理。
这可以通过多种方式进行管理。
-
保护子元素免受父元素
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 的触摸事件