2分钟把握核心
到一些解释android事件分发的,基本都是穷举一些所有情况整几个大图,名词概念搬一搬,也不说为什么会这样,很长,实战或面试的时候能拿来用吗?本人不才,认为以下的伪代码基本能搞定事件分发,自己自定义view的时候也可以直接跟着用,父与子往下传递,你从Activity到DecorView开始分析,实战岂不是得从盘古开天开始讲起吗?我们源码直接干,下面是简化源码
if (!禁止打断()&& 是否打断()) {
viewgroup可以要了
} else {
for (int i = 0; i < childs.size(); i++) {
if (somechild.要//即somechild.onTouchEvent) {
return true;
}
}
}
return viewgroup可以要了
父想要咋办
方案1: 使"!禁止打断()&& 是否打断()" 为true
方案2: child.要全为false
子想要咋办
方案3: 禁止打断为true
方案4: 要的那个view的child.要为true
完事 so easy 如果还没有概括不了情况 欢迎指正
一般如果你理解了上面,事件分发基本你已经悟到了
如今开发10年过去 上述里面就没有做不了的自定义view,可以运用各种复杂的事件
到此下课!下面可以不看,直接去对抗面试官
1分钟看懂案例
说了半天打断?是谁打断 是父布局 onInterceptTouchEvent
1. 方案1场景举例使用
NoScrollViewPager
2. 方案2场景举例使用
自然场景 父布局自行消费
3. 方案3场景举例使用
产品让你在竖着滑动的ScrollView/RV,上面加个横滑的banner,你突然发现横滑有点子卡,本来是子不要父才有。但父先具备的滑动能力,他选择了打断,但此时子又不能完全抢占,竖滑是必须,所以判断滑动方向,且在新
加的banner时候,使用下面方法 动态抢占和释放
parent.requestDisallowInterceptTouchEvent(disallowIntercept)
4. 方案4场景举例使用
父布局是item有自己的点击事件 跳A页面,但这里面有个头像又跳B 详情,此时给双方都加上点击事件即可,子自己优先消费了,不会冲突,你也许会问,两个人都在抢点击,父布局的其他部分怎么就不冲突了呢
TouchEvent的前提是判断的x,y 你都不在一个地方冲突个鸡儿,方案3 banner之外的滑动他又影响不了
再次下课!下面可以不看,直接去自定义任何高级view
以下为伪代码可以不看
public class IView {
public boolean dispatchTouchEvent() {
return onTouchEvent();
}
public boolean onTouchEvent() {
//可以通过setOnTouchListener等消费则 return true
return false;
}
}
public class IViewGroup extends IView {
public List<IView> childs = new ArrayList<>();//
//注释1:理解事件分发有个容易被忽视的前提就是ViewGroup是继承的View
//虽然ViewGroup没有实现onTouchEvent 但是super.dispatchTouchEvent调用的就是super.onTouchEvent 即他自己的onTouchEvent
public boolean onInterceptTouchEvent() {
return false;//
}
@Override
public boolean dispatchTouchEvent() {
if (true) {
//这个true就是
// final int action = ev.getAction();
// final int actionMasked = action & MotionEvent.ACTION_MASK;
// actionMasked == MotionEvent.ACTION_DOWN
resetTouchState();//FLAG_DISALLOW_INTERCEPT==false
//这里的意思就是findid后 直接调requestDisallowInterceptTouchEvent没有暖用
//此处被重置 为false需要在这个if后面调
}
if (!FLAG_DISALLOW_INTERCEPT && onInterceptTouchEvent()) { //只有FLAG_DISALLOW_INTERCEPT为false才去判断onInterceptTouchEvent
super.dispatchTouchEvent();//注释1
} else {
for (int i = 0; i < childs.size(); i++) {
if (childs.get(i).dispatchTouchEvent()) {
return true;//只有一个child View的dispatchTouchEvent(注释1)返回true
//那么后续的view也拿不到 super(他自己)也拿不到
}
}
}
return super.dispatchTouchEvent();
}
private boolean FLAG_DISALLOW_INTERCEPT = false; //是否禁止打断
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
FLAG_DISALLOW_INTERCEPT = disallowIntercept;
//如果viewgroup有父就 parent.requestDisallowInterceptTouchEvent(disallowIntercept)
}
public void resetTouchState() {
FLAG_DISALLOW_INTERCEPT = false;
}
}
如果你还想考虑到down move up 伪代码二次优化。
View mTarget=null;//保存捕获Touch事件处理的View
public boolean dispatchTouchEvent(MotionEvent ev) {
//....其他处理,在此不管
if(ev.getAction()==KeyEvent.ACTION_DOWN){
//每次Down事件,都置为Null
if(FLAG_DISALLOW_INTERCEPT ||!onInterceptTouchEvent()){
mTarget=null;
View[] views=getChildView();
for(int i=0;i<views.length;i++){
if(views[i].dispatchTouchEvent(ev))
mTarget=views[i];
return true;
}
}
}
//当子View没有捕获down事件时,ViewGroup自身处理。这里处理的Touch事件包含Down、Up和Move
if(mTarget==null){
return super.dispatchTouchEvent(ev);
}
//...其他处理,在此不管
if(onInterceptTouchEvent()){
//...其他处理,在此不管
}
//这一步在Action_Down中是不会执行到的,只有Move和UP才会执行到。
return mTarget.dispatchTouchEvent(ev);
}