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

手写Android事件分发

2019-06-16 23:53 1736 查看

手写Android事件分发

Android

事件
分发原理
搞清楚
可以辅助我们解决很多实际项目中遇到的
事件冲突
等问题

进入正题之前,问大家几个事件相关的问题?

Q:

1、
Android事件分发类型有哪几个状态
A:onTouchDown,onTouchMove,onTouchCancel,onTouchUp

Q:

2、
Android点击事件传递规则是怎样的?
下面这段话仔细阅读2遍,有助于加深对事件传递的理解

A:当点击事件产生后,会由
Activity
来处理,传递给
PhoneWindow
,再传递给
DecorView
,最后传递给顶层的
ViewGroup
。对于根
ViewGroup
,点击事件首先传递给它的
dispatchTouchEvent()
方法,如果该
ViewGroup
onInterceptTouchEvent()
方法返回true,则表示它要拦截这个事件,这个事件就会交给它的
onTouchEvent()
方法处理。如果
onInterceptTouchEvent()
方法返回
false
,则表示它不拦截这个事件,则这个事件会交给它的子元素的
dispatchTouchEvent()
来处理,如此反复下去。如果传递给底层的View,View是没有子View的不再需要拦截了,就会调用View的
dispatchTouchEvent()
方法,一般情况下最终会调用View的
onTouchEvent()
方法。接下来讲解点击事件由下而上的传递。当点击事件传给底层的 View 时,如果其
onTouchEvent()
方法返回
true
,则事件由底层的View消耗并处理;如果返回
false
则表示该View不做处理,则传递给父View的
onTouchEvent()
处理;若父View的
onTouchEvent()
仍旧返回false,则继续传递给该父View的父View处理,如此反复下去

Q:

3、
requestDisallowInterceptTouchEvent属性的作用?
A:禁止或允许父View拦截自己的点击事件

// 不允许父View 拦截点击事件
// false则反之
getParent().requestDisallowInterceptTouchEvent(true);

以下通过Java层代码模拟Android中事件分发流程,有助于理解Android事件分发机制,希望能帮助到大家(创建6个java文件,然后以java工程运行打印日志学习)

如果你能静下心来,读完下面6个.java文件代码,我相信你能收获很多

假Activity

public class Activity {

public static void main(String[] arg) {

// 顶级容器ViewGroup(构造函数传递左上,右下坐标)
ViewGroup viewGroup = new ViewGroup(0, 0, 1080, 1920);
viewGroup.setName("顶级容器");

// 二级容器ViewGroup,也是定义两个坐标
ViewGroup viewGroup1 = new ViewGroup(0, 0, 500, 500);
viewGroup1.setName("第二级容器");

// 模拟初始化View以及放置在ViewGroup层级中
View view = new View(0, 0, 200, 200);
view.setName("子View");

viewGroup1.addView(view);
viewGroup.addView(viewGroup1);

viewGroup.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("顶级的OnTouch事件");
return false;
}
});

viewGroup1.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("第二级容器的OnTouch事件");
return false;
}
});

//        view.setOnClickListener(new OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                System.out.println("子iew的onClick事件");
//            }
//        });
//
view.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("子view的OnTouch事件");
return false;
}
});

// 模拟事件分发(点击里面View坐标点为:(100,100))
MotionEvent motionEvent = new MotionEvent(100, 100);
motionEvent.setActionMasked(MotionEvent.ACTION_DOWN);

// 顶级容器分发
viewGroup.dispatchTouchEvent(motionEvent);
}
}

假ViewGroup

public class ViewGroup extends View {

// 子View个数
private View[] mChildren = new View[0];

public ViewGroup(int left, int top, int right, int bottom) {
super(left, top, right, bottom);
}

List<View> childList = new ArrayList<>();

public void addView(View view) {
if (view == null) {
return;
}
childList.add(view);
mChildren = childList.toArray(new View[childList.size()]);
}

private TouchTarget mFirstTouchTarget;

// 事件分发的入口
public boolean dispatchTouchEvent(MotionEvent event) {
//
System.out.println(name + " dispatchTouchEvent ");

boolean handled = false;
boolean intercepted = onInterceptTouchEvent(event);

// TouchTarget  模式 内存缓存   move up
TouchTarget newTouchTarget = null;
int actionMasked = event.getActionMasked();

if (actionMasked != MotionEvent.ACTION_CANCEL && !intercepted) {
// 不拦截情况下,开始处理Down事件
if (actionMasked == MotionEvent.ACTION_DOWN) {
final View[] children = mChildren;
//  遍历ViewGroup中所有子View
for (int i = children.length - 1; i >= 0; i--) {
View child = mChildren[i];
// View能够接收到事件
if (!child.isContainer(event.getX(), event.getY())) {
continue;
}
// 能够接受事件  child   分发给他
if (dispatchTransformedTouchEvent(event, child)) {
// View[]  采取了 Message 的方式进行  链表结构
handled = true;
newTouchTarget = addTouchTarget(child);
break;
}
}
}
// 当前的ViewGroup  dispatchTransformedTouchEvent
if (mFirstTouchTarget == null) {
handled = dispatchTransformedTouchEvent(event, null);
}
}
return handled;
}

private TouchTarget addTouchTarget(View child) {
final TouchTarget target = TouchTarget.obtain(child);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
}

// 回收池策略·
private static final class TouchTarget {

public View child;//当前缓存的View

// 回收池(一个对象)
private static TouchTarget sRecycleBin;

private static final Object sRecycleLock = new Object[0];

public TouchTarget next;

// size
private static int sRecycledCount;

// up事件
public static TouchTarget obtain(View child) {
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;
}

public void recycle() {

if (child == null) {
throw new IllegalStateException("已经被回收过了");
}
synchronized (sRecycleLock) {

if (sRecycledCount < 32) {
next = sRecycleBin;
sRecycleBin = this;
sRecycledCount += 1;
}
}
}
}

//分发处理 子控件  View
private boolean dispatchTransformedTouchEvent(MotionEvent event, View child) {
boolean handled = false;
// 当前View消费了
if (child != null) {
handled = child.dispatchTouchEvent(event);
} else {
handled = super.dispatchTouchEvent(event);
}
return handled;
}

/**
* @param ev
* @return 是否拦截点击事件
*/
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}

}

假View

public class View {

public String name;

@Override
public String toString() {
return "" + name;
}

public void setName(String name) {
this.name = name;
}

private int left;
private int top;
private int right;
private int bottom;

private OnTouchListener mOnTouchListener;
private OnClickListener onClickListener;

public void setOnTouchListener(OnTouchListener mOnTouchListener) {
this.mOnTouchListener = mOnTouchListener;
}

public void setOnClickListener(OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}

public View() {
}

public View(int left, int top, int right, int bottom) {
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}

/**
* @param x
* @param y
* @return 是否处于View点击区域内
*/
public boolean isContainer(int x, int y) {
if (x >= left && x < right && y >= top && y < bottom) {
return true;
}
return false;
}

// 接受分发的代码
public boolean dispatchTouchEvent(MotionEvent event) {
System.out.println(name + " dispatchTouchEvent ");
// 消费
boolean result = false;
if (mOnTouchListener != null && mOnTouchListener.onTouch(this, event)) {
result = true;
}
if (!result && onTouchEvent(event)) {
result = true;
}

return result;
}

private boolean onTouchEvent(MotionEvent event) {
System.out.println(name + " onTouchEvent ");

if (onClickListener != null) {
onClickListener.onClick(this);
return true;
}
return false;
}

}

假MotionEvent

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 actionMasked;
private int x;
private int y;

public MotionEvent() {
}

public MotionEvent(int x, int y) {
this.x = x;
this.y = y;
}

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;
}

public int getActionMasked() {
return actionMasked;
}

public void setActionMasked(int actionMasked) {
this.actionMasked = actionMasked;
}
}

假OnClickListener

public interface OnClickListener {

void onClick(View v);

}

假OnTouchListener

public interface OnTouchListener {

boolean onTouch(View v, MotionEvent event);

}
参考
  1. 解惑requestDisallowInterceptTouchEvent
  2. Android TouchEvent之requestDisallowInterceptTouchEvent
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: