本文参考源码,简单模拟了View事件的传递过程。
package com.log.touchevent;
import com.log.touchevent.listener.MotionEvent;
import com.log.touchevent.listener.OnClickListener;
import com.log.touchevent.listener.OnTouchListener;
public class View {
protected String name;
protected int left;
protected int top;
protected int right;
protected int bottom;
protected OnClickListener onClickListener;
protected OnTouchListener onTouchListener;
public View() {
}
public View(String name, int left, int top, int right, int bottom) {
this.name = name;
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public boolean dispatchTouchEvent(MotionEvent event) {
System.out.println(name + "======>dispatchTouchEvent");
boolean result = false;
if (onTouchListener != null && onTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}
return result;
}
public boolean onTouchEvent(MotionEvent event) {
System.out.println(name + "======>onTouchEvent");
if (onClickListener != null) {
onClickListener.onClick(this);
return true;
}
return false;
}
// 点击坐标是否在view身上
protected boolean isContainer(int x, int y) {
if (x >= left && x <= right && y <= bottom && y >= top) {
return true;
}
return false;
}
public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
public void setOnTouchListener(OnTouchListener onTouchListener) {
this.onTouchListener = onTouchListener;
}
}
package com.log.touchevent;
import com.log.touchevent.listener.MotionEvent;
import java.util.ArrayList;
import java.util.List;
public class ViewGroup extends View {
private List<View> childList = new ArrayList<>();
private View[] mChildren = new View[0];
private TouchTarget mFirstTouchTarget;
public ViewGroup(String name, int left, int top, int right, int bottom) {
super(name, left, top, right, bottom);
}
public void addView(View v) {
childList.add(v);
mChildren = childList.toArray(new View[childList.size()]);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
boolean intercept = onInterceptTouchEvent(event);
boolean handled = false;
TouchTarget newTouchTarget = null;
int aciton = event.getAction();
// 如果不是取消以及本ViewGroup没来拦截事件
if (aciton != MotionEvent.ACTION_CANCEL && !intercept) {
if (aciton == MotionEvent.ACTION_DOWN) {
final View[] children = this.mChildren;
// 扫描所有个子View,找到哪个子View应该会被分发到事件(检查子View是否在点击区域内)。
// 从后往前遍历,是因为最上层的子View处理Touch事件的概率更大些,
// 还有就是如果事件在上层被消费了,那么就不会继续把事件传递回父容器
for (int i = children.length - 1; i >= 0; i--) {
View child = children[i];
// 如果该View不在点击的区域,那么说明它不能处理点击事件
if (!child.isContainer(event.getX(), event.getY())) {
continue;
}
// 把事件分发给能够接收事件的子View
if (dispatchTransformedTouchEvent(event, child)) {
// 如果事件被该子View消费了
handled = true;
newTouchTarget = addTouchTarget(child);
break;
}
}
}
}
// 如果没有子View处理事件,那么就调用自己的处理方法
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(event, null);
}
return handled;
}
private TouchTarget addTouchTarget(View child) {
TouchTarget touchTarget = TouchTarget.obtain(child);
touchTarget.next = mFirstTouchTarget;
mFirstTouchTarget = touchTarget;
return touchTarget;
}
private boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {
System.out.println(name + "======>dispatchTransformedTouchEvent");
boolean handled;
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
return handled;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
System.out.println(name + "======>onInterceptTouchEvent");
return false;
}
public static final class TouchTarget {
// 最大的链表数量(缓存数量)
private static final int MAX_RECYCLED = 32;
private static final Object sRecycleLock = new Object[0];
public static TouchTarget sRecycleBin; // 链表表头
public static int sRecycledCount; // 当前链表元素个数
public View child;
public TouchTarget next; // 指向下一个元素
private TouchTarget() {
}
public static TouchTarget obtain(View child) {
final TouchTarget target;
synchronized (sRecycleLock) {
if (sRecycleBin == null) {
target = new TouchTarget();
} else {
target = sRecycleBin;
sRecycleBin = target.next;
sRecycledCount--;
target.next = null;
}
target.child = child;
}
return target;
}
// 回收当前TouchTarget,重新放入链表
public void recycle() {
if(child == null){
throw new IllegalStateException("already recycled once");
}
if (sRecycledCount < MAX_RECYCLED) {
next = sRecycleBin;
sRecycleBin = this;
sRecycledCount++;
} else {
next = null;
}
child = null;
}
}
}
package com.log.touchevent.listener;
public class MotionEvent {
public static final int ACTION_DOWN = 0;
public static final int ACTION_UP = 1;
public static final int ACTION_MOVE = 2;
public static final int ACTION_CANCEL = 3;
private int mAction;
private int x;
private int y;
public MotionEvent(int action, int x, int y) {
this.mAction = action;
this.x = x;
this.y = y;
}
public int getAction() {
return mAction;
}
public void setAction(int mAction) {
this.mAction = mAction;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
package com.log.touchevent.listener;
import com.log.touchevent.View;
public interface OnClickListener {
void onClick(View v);
}
package com.log.touchevent.listener;
import com.log.touchevent.View;
public interface OnTouchListener {
boolean onTouch(View v, MotionEvent event);
}
Activity点击事件触发顺序:
Activity.dispatchTouchEvent() ==> PhoneWindow.superDispatchTouchEvent() ==> mDecorView.dispatchTouchEvent() ==> containerView.dispatchTouchEvent() ==> Activity.setContentView()设置的View.dispatchTouchEvent()
我们这里只是简单模拟下顶层触发传递点击事件:
package com.log.touchevent;
import com.log.touchevent.listener.MotionEvent;
import com.log.touchevent.listener.OnClickListener;
import com.log.touchevent.listener.OnTouchListener;
public class Activity {
public static void main(String[] args) {
MotionEvent event = new MotionEvent(MotionEvent.ACTION_DOWN, 200, 200);
ViewGroup viewGroup = new ViewGroup("顶级容器", 0, 0, 1000, 1000);
ViewGroup viewGroup2 = new ViewGroup("二级容器", 100, 100, 600, 600);
View view = new View("子View", 200, 200, 500, 500);
viewGroup.addView(viewGroup2);
viewGroup2.addView(view);
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
System.out.println(v.name + "=====***>OnClickListener");
}
});
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println(v.name + "=====***>setOnTouchListener");
return true;
}
});
// 模拟顶层触发传递点击事件
viewGroup.dispatchTouchEvent(event);
}
}
打印结果: