如何在子视图和父视图组触摸事件之间变化
How to vary between child and parent view group touch events
我决定post这个问题并回答这个问题:
我会把评论贴在这里:
Suppose I want to override the touch events only for handling some of
the children, what can I do inside this function to have it working ?
I mean, for some children it would work as usual, and for some, the
parent-view will decide if they will get the touch events or not.
所以问题是:如何防止父元素 onTouchEvent()
覆盖某些子元素 onTouchEvent()
,同时覆盖其他子元素?
- 嵌套视图组的
onTouchEvents()
可以由 boolean
onInterceptTouchEvent 管理。
OnInterceptTouchEvent
的默认值为 false。
parent 的 onTouchEvent
在 child 之前收到。如果 OnInterceptTouchEvent
return 为假,它会将运动事件沿链发送到 child 的 OnTouchEvent
处理程序。如果 return 为真,parent 将处理触摸事件。
然而,在某些情况下,我们可能希望一些 child 元素管理 OnTouchEvent
s 而一些由 parent 视图(或者可能 parent parent).
这可以通过不止一种方式进行管理。
- 可以保护 child 元素不受 parent 的
OnInterceptTouchEvent
影响的一种方法是实施 requestDisallowInterceptTouchEvent.
public void requestDisallowInterceptTouchEvent (boolean
disallowIntercept)
这会阻止任何 parent 视图管理此元素的 OnTouchEvent
,如果该元素启用了事件处理程序的话。
- 如果
OnInterceptTouchEvent
为假,将计算 child 元素的 OnTouchEvent
。如果您在 child 元素中有处理各种触摸事件的方法,则任何被禁用的相关事件处理程序都会 return OnTouchEvent 到 parent。
这个答案:
很好地可视化了触摸事件的传播方式:
parent -> child|parent -> child|parent -> child views.
- 另一种方法是 return 将
OnInterceptTouchEvent
的不同值用于 parent。
此示例取自 Managing Touch Events in a ViewGroup,演示了如何在用户滚动时拦截 child 的 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;
}
编辑:回复评论。
这是来自同一 link 的一些代码,展示了如何创建元素周围矩形的参数:
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);
}
让我们修改问题。
你碰巧有一个 ViewGroup 和一堆 children。您想要拦截此 ViewGroup 中所有内容的触摸事件,但一些 children.
的小例外
我一直在寻找同一个问题的答案很长一段时间。没有设法找到任何合理的东西,因此我自己提出了以下解决方案。
以下代码片段概述了 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;
}
我决定post这个问题并回答
我会把评论贴在这里:
Suppose I want to override the touch events only for handling some of the children, what can I do inside this function to have it working ? I mean, for some children it would work as usual, and for some, the parent-view will decide if they will get the touch events or not.
所以问题是:如何防止父元素 onTouchEvent()
覆盖某些子元素 onTouchEvent()
,同时覆盖其他子元素?
- 嵌套视图组的
onTouchEvents()
可以由boolean
onInterceptTouchEvent 管理。
OnInterceptTouchEvent
的默认值为 false。
parent 的 onTouchEvent
在 child 之前收到。如果 OnInterceptTouchEvent
return 为假,它会将运动事件沿链发送到 child 的 OnTouchEvent
处理程序。如果 return 为真,parent 将处理触摸事件。
然而,在某些情况下,我们可能希望一些 child 元素管理 OnTouchEvent
s 而一些由 parent 视图(或者可能 parent parent).
这可以通过不止一种方式进行管理。
- 可以保护 child 元素不受 parent 的
OnInterceptTouchEvent
影响的一种方法是实施 requestDisallowInterceptTouchEvent.
public void requestDisallowInterceptTouchEvent (boolean disallowIntercept)
这会阻止任何 parent 视图管理此元素的 OnTouchEvent
,如果该元素启用了事件处理程序的话。
- 如果
OnInterceptTouchEvent
为假,将计算 child 元素的OnTouchEvent
。如果您在 child 元素中有处理各种触摸事件的方法,则任何被禁用的相关事件处理程序都会 return OnTouchEvent 到 parent。
这个答案:
很好地可视化了触摸事件的传播方式:
parent -> child|parent -> child|parent -> child views.
- 另一种方法是 return 将
OnInterceptTouchEvent
的不同值用于 parent。
此示例取自 Managing Touch Events in a ViewGroup,演示了如何在用户滚动时拦截 child 的 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;
}
编辑:回复评论。
这是来自同一 link 的一些代码,展示了如何创建元素周围矩形的参数:
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);
}
让我们修改问题。
你碰巧有一个 ViewGroup 和一堆 children。您想要拦截此 ViewGroup 中所有内容的触摸事件,但一些 children.
的小例外我一直在寻找同一个问题的答案很长一段时间。没有设法找到任何合理的东西,因此我自己提出了以下解决方案。
以下代码片段概述了 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;
}