文档章节

android事件分发机制详解

abcijkxyz
 abcijkxyz
发布于 2016/07/30 17:23
字数 999
阅读 4
收藏 0
先讲View:
只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,看下该函数的实现:
public boolean dispatchTouchEvent(MotionEvent event) {  
    if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
            mOnTouchListener.onTouch(this, event)) {  
        return true;
    }
    return onTouchEvent(event);  
}
可以看出一个Enable的控件设置了OnTouchListener,如果在onTouch()里面返回了true,那么触碰事件就被onTouch()“消费”掉了,不会走到onTouchEvent();如果返回false,那么就会往下走,这样到了onTouchEvent()方法里面,其实现如下:
public boolean onTouchEvent(MotionEvent event) {  
    ...
    if (((viewFlags & CLICKABLE) == CLICKABLE ||  
            (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
        switch (event.getAction()) {  
            case MotionEvent.ACTION_UP:
				...
                performClick();                
                break;  
            case MotionEvent.ACTION_DOWN:
                break;  
            case MotionEvent.ACTION_CANCEL:  
                break;  
            case MotionEvent.ACTION_MOVE:  
                break;  
        }  
        return true;  
    }  
    return false;  
}
可以看到,如果一个控件是clickable的,那么就会走到ACTION_UP,ACTION_DOWN等case里面,并且最终返回true。需要说明的是:如果在上一个case(比如:ACTION_UP)返回了false,那么其下面所有的case(比如:ACTION_CANCEL、ACTION_MOVE等)都不会得到执行。在ACTION_UP下面调用了performClick()方法,其实现如下:
public boolean performClick() {  
    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
    if (mOnClickListener != null) {  
        playSoundEffect(SoundEffectConstants.CLICK);  
        mOnClickListener.onClick(this);  
        return true;  
    }  
    return false;  
}
在这里我们就知道了为什么点击一个按钮会执行onClick()方法。
总结一下,总的调用顺序:
dispatchTouchEvent-->onTouch()[需要返回false才会继续往下]-->onTouchEvent()[需要控件是clickable才会继续往下走]-->onClick()。

再看看ViewGroup的事件分发,ViewGroup是什么呢?
ViewGroup就是一组View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父类或间接父类,像LinearLayout、RelativeLayout等都是继承自ViewGroup的。但ViewGroup实际上也是一个View,只不过比起View,它多了可以包含子View和定义布局参数的功能。
面前说点击某一个控件,会调用到该控件的dispatchTouchEvent方法,实际是这样的:当你点击了某个控件,首先会去调用该控件所在布局的dispatchTouchEvent方法,然后在布局的dispatchTouchEvent方法中找到被点击的相应控件,再去调用该控件的dispatchTouchEvent方法。
下面来看下ViewGroup的dispatchTouchEvent()方法的实现吧:
public boolean dispatchTouchEvent(MotionEvent ev) {
	...    
	if (disallowIntercept || !onInterceptTouchEvent(ev)) {		
		final int count = mChildrenCount;
		for (int i = count - 1; i >= 0; i--) {
			final View child = children[i];
			...
			if (child.dispatchTouchEvent(ev))  {
				mMotionTarget = child;
				return true;
			}
		}
	}
    ...
    if (target == null) {
        ...
        return super.dispatchTouchEvent(ev);
    }
    ...
    return target.dispatchTouchEvent(ev);
}
可以看到里面调用了一个函数onInterceptTouchEvent(),这个是什么东东呢?看下实现:
public boolean onInterceptTouchEvent(MotionEvent ev) {  
    return false;  
}
该函数就是说,父视图是否拦截子视图的touch事件,返回false,说明默认是不拦截的。由于此处返回是的false,所以会走到子view的dispatchTouchEvent()方法,这个方法上面我们详细分析过了。如果点击的是父视图的空白区域,或是onInterceptTouchEvent返回true,那么就会走到第17行,这里的super当然就View啦,就又回到了上面我们讲过的View的分发流程。
看一个例子:
public class MyLayout extends LinearLayout {  
  
    public MyLayout(Context context, AttributeSet attrs) {  
        super(context, attrs);  
    }  
	
	@Override  
    public boolean onInterceptTouchEvent(MotionEvent ev) {  
        return true;  
    }   
}  
myLayout.setOnTouchListener(new OnTouchListener() {  
    @Override  
    public boolean onTouch(View v, MotionEvent event) {  
        Log.d("TAG", "myLayout on touch");  
        return false;  
    }  
});  
button1.setOnClickListener(new OnClickListener() {  
    @Override  
    public void onClick(View v) {  
        Log.d("TAG", "You clicked button1");  
    }  
});

该例子中,MyLayout布局里面包含了一个button1,并且为myLayout和button1都添加了OnTouchListener,如果我们重写的onInterceptTouchEvent返回true,那么touch事件就被myLayout拦截了,不会传到button1上面,反之会则会传到button1上面,当然点击空白区域还是会被myLayout捕获的。

下面用一张图来总结下这个流程:


在解决事件冲突中的运用:

比如scrollview嵌套viewpager导致的滑动冲突
有两种解决方法:
1、重写scrollview类的onInterceptTouchEvent方法,如果发现是水平滑动的话,那就在该方法内返回false,这样就不拦截viewpager了,会执行viewpager的onTouchEvent方法;
2、重写viewpager中的onTouchEvent方法,如果发现水平滑动就设置getParent().requestDisallowInterceptTouchEvent(true)来通知父控件不要拦截事件,如果是竖直滑动就设置为false。

本文转载自:http://blog.csdn.net/wdong_love_cl/article/details/51477607

共有 人打赏支持
abcijkxyz
粉丝 63
博文 6196
码字总数 1876
作品 0
深圳
项目经理
私信 提问
Android 机制篇 - 事件分发机制超详解(🔥🔥🔥🔥🔥🔥🔥🔥)

Android 虽然不是四大组件,但其并不比四大组件的地位低(涉及面的广度和深入甚至比四大组件还复杂🔥)。而View的核心知识点“事件分发机制”则是不少刚入门同学的拦路虎(1、项目中处处遇...

Pepsimaxin
07/12
0
0
C语言自学完备手册(21)——递归

版权声明: https://blog.csdn.net/lfdfhl/article/details/82897146 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–o...

谷哥的小弟
09/29
0
0
C语言自学完备手册(19)——冒泡排序

版权声明: https://blog.csdn.net/lfdfhl/article/details/82862688 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–o...

谷哥的小弟
09/27
0
0
PDF转Word的利器——ABBYY FineReader

版权声明: https://blog.csdn.net/lfdfhl/article/details/84960682 自定义View系列教程00–推翻自己和过往,重学自定义View 自定义View系列教程01–常用工具介绍 自定义View系列教程02–o...

谷哥的小弟
12/11
0
0
【Android】打造下拉放大效果

前言 在其他App上看到了这样的一个效果,感觉有点意思,于是决定实现一个类似的效果。 (其实是iOS的同学在是现功能的时候秀了一波操作) 效果大概是这样子的: UI看完后 “这个效果不错啊”...

带心情去旅行
06/19
0
0

没有更多内容

加载失败,请刷新页面

加载更多

RestClientUtil和ConfigRestClientUtil区别说明

RestClientUtil directly executes the DSL defined in the code. ConfigRestClientUtil gets the DSL defined in the configuration file by the DSL name and executes it. RestClientUtil......

bboss
今天
11
0

中国龙-扬科
昨天
2
0
Linux系统设置全局的默认网络代理

更改全局配置文件/etc/profile all_proxy="all_proxy=socks://rahowviahva.ml:80/"ftp_proxy="ftp_proxy=http://rahowviahva.ml:80/"http_proxy="http_proxy=http://rahowviahva.ml:80/"......

临江仙卜算子
昨天
9
0
java框架学习日志-6(bean作用域和自动装配)

本章补充bean的作用域和自动装配 bean作用域 之前提到可以用scope来设置单例模式 <bean id="type" class="cn.dota2.tpye.Type" scope="singleton"></bean> 除此之外还有几种用法 singleton:......

白话
昨天
8
0
在PC上测试移动端网站和模拟手机浏览器的5大方法

总结很全面,保存下来以备不时之需。原文地址:https://www.cnblogs.com/coolfeng/p/4708942.html

kitty1116
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部