您的位置:首页 > 移动开发 > Android开发

Android View事件分发机制 二(ViewGroup)

2017-09-12 14:23 537 查看
在上篇文章中,我们讨论了
View
的事件分发机制。这篇文章我们讨论布局控件
ViewGroup
的事件分发机制。
ViewGroup
继承自
View
组件。

一、小例子分析

这次由于要讨论布局控件
ViewGroup
,所以这次我们重写
Button
LinearLayout
两个控件。

1.项目源码

自定义Button类

public class TestButton extends Button {

public static final String CLICK_EXAMPLE_3 = "ClickExample3";

public TestButton(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {

Log.i(CLICK_EXAMPLE_3,"TestButton dispatchTouchEvent--action= "+event.getAction());
return super.dispatchTouchEvent(event);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(CLICK_EXAMPLE_3,"TestButton onTouchEvent--action= "+event.getAction());
return super.onTouchEvent(event);
}
}


自定义LinearLayout类

public class TestLinearLayout extends LinearLayout {

public static final String CLICK_EXAMPLE_3 = "ClickExample3";

public TestLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(CLICK_EXAMPLE_3,"TestLinearLayout onInterceptTouchEvent--action= "+ev.getAction());
return super.onInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(CLICK_EXAMPLE_3,"TestLinearLayout onTouchEvent--action= "+event.getAction());
return super.onTouchEvent(event);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(CLICK_EXAMPLE_3,"TestLinearLayout dispatchTouchEvent--action= "+ev.getAction());
return super.dispatchTouchEvent(ev);
}
}


主布局文件:

<?xml version="1.0" encoding="utf-8"?>
<com.lengyu.free.clickexample3.TestLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/my_linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"

>

<com.lengyu.free.clickexample3.TestButton
android:id="@+id/my_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me!"/>
</com.lengyu.free.clickexample3.TestLinearLayout>


Activity
文件

public class MainActivity extends Activity  implements View.OnClickListener,View.OnTouchListener{

public static final String CLICK_EXAMPLE_3 = "ClickExample3";
private  TestLinearLayout mLayout;
private  TestButton mButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mButton=(TestButton)this.findViewById(R.id.my_btn);
mLayout=(TestLinearLayout)this.findViewById(R.id.my_linear);
mButton.setOnClickListener(this);
mLayout.setOnClickListener(this);

mButton.setOnTouchListener(this);
mLayout.setOnTouchListener(this);
}

@Override
public void onClick(View v) {
Log.i(CLICK_EXAMPLE_3,"OnClickListener--onClick--"+v);
}

@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(CLICK_EXAMPLE_3,"OnTouchListener--onTouch--action="+event.getAction()+"--"+v);
return false;
}
}


2.事件分析

事件1: 点击
Button
区域:

09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 0
09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 0
09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestButton dispatchTouchEvent--action= 0
09-12 02:21:11.127 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=0--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ......I. 166,345-313,417 #7f0c0051 app:id/my_btn}
09-12 02:21:11.127 2292-2292/? I/ClickExample3: TestButton onTouchEvent--action= 0
09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 1
09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 1
09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestButton dispatchTouchEvent--action= 1
09-12 02:21:11.177 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=1--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ...P..I. 166,345-313,417 #7f0c0051 app:id/my_btn}
09-12 02:21:11.177 2292-2292/? I/ClickExample3: TestButton onTouchEvent--action= 1
09-12 02:21:11.177 2292-2292/? I/ClickExample3: OnClickListener--onClick--com.lengyu.free.clickexample3.TestButton{b2f60670 VFED..C. ...P..I. 166,345-313,417 #7f0c0051 app:id/my_btn}


可以发现,点击button时,触发的流程是:
TestLinearLayout.dispatchTouchEvent
->
TestLinearLayout.onInterceptTouchEvent
->
TestButton.dispatchTouchEvent
,也就是说点击
TestButton
时,分发的每一个事件都是由
TestButton
的父控件
TestLinearLayout
分发给子控件。

事件2: 点击
Button
区域之外的区域:

09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 0
09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout onInterceptTouchEvent--action= 0
09-12 02:54:56.138 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=0--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ......I. 0,0-480,762 #7f0c0050 app:id/my_linear}
09-12 02:54:56.138 2292-2292/? I/ClickExample3: TestLinearLayout onTouchEvent--action= 0
09-12 02:54:56.218 2292-2292/? I/ClickExample3: TestLinearLayout dispatchTouchEvent--action= 1
09-12 02:54:56.218 2292-2292/? I/ClickExample3: OnTouchListener--onTouch--action=1--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ...P..I. 0,0-480,762 #7f0c0050 app:id/my_linear}
09-12 02:54:56.218 2292-2292/? I/ClickExample3: TestLinearLayout onTouchEvent--action= 1
09-12 02:54:56.218 2292-2292/? I/ClickExample3: OnClickListener--onClick--com.lengyu.free.clickexample3.TestLinearLayout{b2f5fee8 V.E...C. ...P..I. 0,0-480,762 #7f0c0050 app:id/my_linear}


可以看到,点击
button
之外的区域时,
ACTION_DOWN
事件的分发顺序是
dispatchTouchEvent
->
onInterceptTouchEvent
->
onTouch
->
onTouchEvent
ACTION_UP
事件的分发顺序是
dispatchTouchEvent
->
onTouch
->
onTouchEvent
-
onClick
,在执行
ACTION_UP
事件时,
onInterceptTouchEvent
并没有执行。

这时需要我们从
ViewGroup
中寻找答案。

二、ViewGroup源码分析

ViewGroup
继承自
View
,对其中的部分方法进行了复写。下面我们先来分析一下
dispatchTouchEvent
方法

2.1
dispatchTouchEvent
方法

ViewGroup
dispatchTouchEvent
方法如下:

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
}

// If the event targets the accessibility focused view and this is it, start
// normal event dispatch. Maybe a descendant is what will handle the click.
if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
ev.setTargetAccessibilityFocus(false);
}

boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;

// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}

// Check for interception.
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN
|| mFirstTouchTarget != null) {
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}
} else {
// There are no touch targets and this action is not an initial down
// so this view group continues to intercept touches.
intercepted = true;
}

// If intercepted, start normal event dispatch. Also if there is already
// a view that is handling the gesture, do normal event dispatch.
if (intercepted || mFirstTouchTarget != null) {
ev.setTargetAccessibilityFocus(false);
}

// Check for cancelation.
final boolean canceled = resetCancelNextUpFlag(this)
|| actionMasked == MotionEvent.ACTION_CANCEL;

// Update list of touch targets for pointer down, if needed.
final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {

// If the event is targeting accessiiblity focus we give it to the
// view that has accessibility focus and if it does not handle it
// we clear the flag and dispatch the event to all children as usual.
// We are looking up the accessibility focused host to avoid keeping
// state since these events are very rare.
View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
? findChildWithAccessibilityFocus() : null;

if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
final int actionIndex = ev.getActionIndex(); // always 0 for down
final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
: TouchTarget.ALL_POINTER_IDS;

// Clean up earlier touch targets for this pointer id in case they
// have become out of sync.
removePointersFromTouchTargets(idBitsToAssign);

final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// Find a child that can receive the event.
// Scan children from front to back.
final ArrayList<View> preorderedList = buildOrderedChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex);

// If there is a view that has accessibility focus we want it
// to get the event first and if not handled we will perform a
// normal dispatch. We may do a double iteration but this is
// safer given the timeframe.
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}

if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}

newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}

resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}

// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}
if (preorderedList != null) preorderedList.clear();
}

if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}

// Dispatch to touch targets.
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it.  Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}

// Update list of touch targets for pointer up or cancel, if needed.
if (canceled
|| actionMasked == MotionEvent.ACTION_UP
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
resetTouchState();
} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
final int actionIndex = ev.getActionIndex();
final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
removePointersFromTouchTargets(idBitsToRemove);
}
}

if (!handled && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
}
return handled;
}


1. 19行-25 行,对
ACTION_DOWN
事件进行处理。

由于
ACTION_DOWN
事件是一系类事件的开端,所以在首次分发
ACTION_DOWN
事件时,调用
cancelAndClearTouchTargets(ev);
方法,对
ACTION_DOWN
事件之前的手势进行清理取消,并将
mFirstTouchTarget
的值设为
NULL
。然后调用
resetTouchState
方法,将触摸状态重置。

2. 28 行-42 行,检测是否需要拦截

在该处代码中,先声明了一个
intercepted
变量,用来标记是否需要拦截。然后在if判断中,判断
actionMasked == MotionEvent.ACTION_DOWN

|| mFirstTouchTarget != null
, 如果是
ACTION_DOWN
或者
mFirstTouchTarget != null
(即已经找到能够接收touch事件的目标组件)时,条件成立,成立后接下来获取
mGroupFlags
中 的
disallowIntercept
(禁止拦截)状态,如果
disallowIntercept
(禁止拦截)的值的状态为
false
的话,即允许拦截,则调用
onInterceptTouchEvent
方法获取用户自定义的拦截状态设置给
intercepted
变量,并将action的行为进行存储。如果
disallowIntercept
(禁止拦截)的值的状态为
true
的话,即不允许拦截的话,则直接设置
intercepted
变量的值为
false
。如果
actionMasked == MotionEvent.ACTION_DOWN

|| mFirstTouchTarget != null
不成立,即尚未找到触摸对象且不是初始化的
ACTION_DOWN
事件,则直接进行拦截。

其中
disallowIntercept
(禁止拦截) 状态的设置可用
requestDisallowInterceptTouchEvent
方法

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
// We're already in this state, assume our ancestors are too
return;
}

if (disallowIntercept) {
mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
} else {
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
}

// Pass it up to our parent
if (mParent != null) {
mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
}
}


requestDisallowInterceptTouchEvent
方法设置我
true
时,
intercepted
的值为
false
。ViewGroup不同于View特有的
onInterceptTouchEvent
方法

public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}


默认的
onInterceptTouchEvent
方法只是返回了一个
false
,也即
intercepted=false
. 上面例子的调用流程为
dispatchTouchEvent
->
onInterceptTouchEvent
->
onTouchEvent
,也就是说
disallowIntercept
的默认值为
false
.

参考资料:

3. 51 行 到 52行

通过标记和action检查cancel,然后将结果赋值给局部boolean变量canceled。

4. 53-函数结束,事件分发。

54行首先可以看见获取一个boolean变量标记split来标记,默认是true,作用是是否把事件分发给多个子View,这个同样在ViewGroup中提供了public的方法设置,如下:

public void setMotionEventSplittingEnabled(boolean split) {
// TODO Applications really shouldn't change this setting mid-touch event,
// but perhaps this should handle that case and send ACTION_CANCELs to any child views
// with gestures in progress when this is changed.
if (split) {
mGroupFlags |= FLAG_SPLIT_MOTION_EVENTS;
} else {
mGroupFlags &= ~FLAG_SPLIT_MOTION_EVENTS;
}
}


接着会有1个
if(!canceled && !intercepted)
判断语句,当
canceled
intercepted
的值都为
false
时,会进入
if
其中。

第80行的
if
判断中对
newTouchTarget
childrenCount
进行判断,如果
newTouchTarget
为空和
childrenCount
个数不为空的话,则进一步处理。

第85行调用
buildOrderedChildList
方法返回
ViewGroup


字view的先序遍历顺序,其中先序遍历顺序是按view的绘制顺序排列的(按照View的Z坐标由小到大排列)

第89行进入1个
for
循环,从
buildOrderedChildList
序列的最后一个
view
向前遍历,视觉上是从前往后进行扫描,即对于多个包含同一点的
view
, 浮在最上面的view最先进行相应

第103行调用
getTouchTarget(child)
方法来判断当前view是否已经在
mFirstTouchTarget
变量所指向的
TouchTarget
链表中,如果在的还返回该对象,如果不在的返回null.

第114行判断getTouchTarget返回的对象是否为null,如果不为null表示当前view已经接收了自已区域内的响应事件直接break出循环。

第122行调用
dispatchTransformedTouchEvent
方法对传递的事件进行分发,该方法如果第3个参数
child
控件的值为null的话,即
ViewGroup
的子
view
为空,调用父控件(View)的
dispatchTouchEvent
方法。子
view
为不为空,调用子控件的
dispatchTouchEvent
方法。该方法返回调用
dispatchTouchEvent
方法的布尔值。

第122行到141行,如果调用
dispatchTransformedTouchEvent
方法时子
view
或父
view
dispatchTouchEvent
返回的值为
true
话,(这时子View中的
onTouchEvent
方法已经消费掉该事件返回true)则进入if条件语句中,在该if条件从句中
mLastTouchDownIndex
记录最后
child
控件的坐标, 调用
addTouchTarget
方法将当前子View加入到
mFirstTouchTarget
变量指向的链表中,
newTouchTarget
记录当前
TouchTarget
, 将
alreadyDispatchedToNewTouchTarget
方法设置为
true
;跳出当前遍历子
view
循环。如果调用
dispatchTransformedTouchEvent
方法时子
view
或父
view
dispatchTouchEvent
返回的值为
false
,这时
newTouchTarget
为null,
alreadyDispatchedToNewTouchTarget
变量的值为
false
,无法调用addTouchTarget(),从而导致
mFirstTouchTarget
为null(没法对mFirstTouchTarget赋值,因为上面分析了
mFirstTouchTarget
一进来是
ACTION_DOWN
就置位为
null
了),这时再执行下面的
ACTION_MOVE
事件和
ACTION_UP
事件时,执行到28行到42行时,
mFirstTouchTarget
为null导致
onInterceptTouchEvent
方法不会执行,
intercepted
的值
true
,再执行到58行时,直接跳到163行执行,把ViewGroup当作普通view调用
dispatchTransformedTouchEvent
方法处理事件。

149行到第159行之间的为for循环遍历
View
之外以及条件
if (newTouchTarget == null && childrenCount != 0)
之后的代码,该处代码判断如果newTouchTarget 为null,但mFirstTouchTarget不为空,即没有找到子view接收控件,但
mFirstTouchTarget
不为空,就将
newTouchTarget
指向
mFirstTouchTarget
链表中最后一个添加的对象。

163行到166行,如果
mFirstTouchTarget=null
,即ViewGroup没有找到能够消费子控件的view对象,则把当前ViewGroup当作普通控件调用父
view
dispatchTouchEvent
方法进行处理。该种情况与上面事件2(点击 Button区域外的区域)情况相同,结合29行到42行代码解释了为什么
ACTIONDOWN
事件调用了
onInterceptTouchEvent
方法,
ACTIONDOWNUP
没有调用。同时也解释了上文章中的事件9,由于子view中处理
ACTION_DOWN
事件时,
dispatchTouchEvent
方法返回为
false
, 导致
dispatchTransformedTouchEvent
返回为
false
,没有对
mFirstTouchTarget
进行赋值为null,在该处代码中将
ACTION_DOWN
事件分发到父
View
中的
dispatchTouchEvent
方法中,多打出父
View
ACTION_DOWN
事件日志。

第174-176行代码是对ACTION_DOWN事件和
dispatchTransformedTouchEvent
返回为
true
的处理,这时
mFirstTouchTarget!=null
已经找到消费该事件的子
View
,故直接返回
true
,不再调用父VIew中的
dispatchTouchEvent
方法。第176-195行代码,则是对已经找到消费该事件子view,除
ACTIONDOWN
事件之外的其它事件进行处理,接着递归调用子view的
dispatchTouchEvent
方法

2.1 dispatchTransformedTouchEvent

dispatchTransformedTouchEvent方法源码如下

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;

// Canceling motions is a special case.  We don't need to perform any transformations
// or filtering.  The important part is the action, not the contents.
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}

// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}

// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);

handled = child.dispatchTouchEvent(event);

event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}

// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}

handled = child.dispatchTouchEvent(transformedEvent);
}

// Done.
transformedEvent.recycle();
return handled;
}


第8行到17行,对
ACTION_CANCEL
事件进行处理,如果第3个参数
child
控件的值为null的话,即
ViewGroup
的子
view
为空,调用父控件(View)的
dispatchTouchEvent
方法。子
view
为不为空,调用子控件的
dispatchTouchEvent
方法

第35行到70行,对 事件进行处理,如果第3个参数
child
控件的值为null的话,即
ViewGroup
的子
view
为空,调用父控件(View)的
dispatchTouchEvent
方法。子
view
为不为空,调用子控件的
dispatchTouchEvent
方法。该方 法返回调用
dispatchTouchEvent
方法的布尔值。

参考资料:

http://blog.csdn.net/yanbober/article/details/45912661
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: