这些方法有很多困惑,但实际上并不那么复杂。大多数的困惑是因为:
如果您View/ViewGroup还是其子的任何不返回真 onTouchEvent,dispatchTouchEvent并且onInterceptTouchEvent将只被调用MotionEvent.ACTION_DOWN。如果没有true from onTouchEvent,则父视图将假定您的视图不需要MotionEvents。
如果ViewGroup的所有子级都没有在onTouchEvent中返回true,则仅会调用onInterceptTouchEvent MotionEvent.ACTION_DOWN,即使您的ViewGroup在中返回true onTouchEvent。
处理顺序如下:
dispatchTouchEvent 叫做。
onInterceptTouchEventMotionEvent.ACTION_DOWN或在ViewGroup的任何子代返回true时被调用onTouchEvent。
onTouchEvent首先在ViewGroup的子级上调用,当所有子级都不返回true时,在上调用 View/ViewGroup。
如果要预览TouchEvents/MotionEvents而不禁用孩子的事件,则必须做两件事:
覆盖dispatchTouchEvent预览事件并返回 super.dispatchTouchEvent(ev);
覆盖onTouchEvent并返回true,否则您将不会获得 MotionEvent 除外MotionEvent.ACTION_DOWN。
如果您想检测某种手势(如滑动事件),而又不禁用孩子的其他事件(只要您没有检测到手势),则可以执行以下操作:
如上所述,预览MotionEvent,并在检测到手势时设置标志。
onInterceptTouchEvent当您的标志设置为取消孩子的MotionEvent处理时,返回true 。这也是重置标志的方便位置,因为onInterceptTouchEvent直到next才会再次调用MotionEvent.ACTION_DOWN。
中的替代FrameLayout示例(我在Xamarin Android上编程时的示例是C#,但Java中的逻辑相同):
public override bool DispatchTouchEvent(MotionEvent e)
{
// Preview the touch event to detect a swipe:
switch (e.ActionMasked)
{
case MotionEventActions.Down:
_processingSwipe = false;
_touchStartPosition = e.RawX;
break;
case MotionEventActions.Move:
if (!_processingSwipe)
{
float move = e.RawX - _touchStartPosition;
if (move >= _swipeSize)
{
_processingSwipe = true;
_cancelChildren = true;
ProcessSwipe();
}
}
break;
}
return base.DispatchTouchEvent(e);
}
public override bool OnTouchEvent(MotionEvent e)
{
// To make sure to receive touch events, tell parent we are handling them:
return true;
}
public override bool OnInterceptTouchEvent(MotionEvent e)
{
// Cancel all children when processing a swipe:
if (_cancelChildren)
{
// Reset cancel flag here, as OnInterceptTouchEvent won't be called until the next MotionEventActions.Down:
_cancelChildren = false;
return true;
}
return false;
}