Android事件传递机制(一)
来源:互联网 发布:怎么提高淘宝流量 编辑:程序博客网 时间:2024/04/28 02:05
Android的事件传递机制是一个很重要的技术,本章节详细讲解Android的事件传递的基础
demo:描述 在一个页面(activity)中有一个按钮(button) 通过button的点击来说明Android事件传递机制
1.只添加Click监听
//点击事件
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d("EventActivity", "点击事件!!!!!!!!!!!");
}
});
打印结果
最基本的操作 再次不过的的赘述
2.只添加onTouch监听
//触摸事件
btn.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
int action=arg1.getAction();
if(action==MotionEvent.ACTION_DOWN){//按下
Log.d("EventActivity", "按下!!!!!!!!!!!");
}else if(action==MotionEvent.ACTION_MOVE){//移动
Log.d("EventActivity", "移动!!!!!!!!!!!");
}else if(action==MotionEvent.ACTION_UP){//抬起
Log.d("EventActivity", "抬起!!!!!!!!!!!");
}
return false;
}
});
打印结果
基本操作 onTouch 按下 移动 抬起 操作
那么要是button既有触摸事件又有点击事件那么操作时先执行那个监听呢
//点击事件
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Log.d("EventActivity", "Click事件!!!!!!!!!!!");
}
});
//触摸事件
btn.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View arg0, MotionEvent arg1) {
int action=arg1.getAction();
if(action==MotionEvent.ACTION_DOWN){//按下
Log.d("EventActivity", "按下!!!!!!!!!!!");
}else if(action==MotionEvent.ACTION_MOVE){//移动
Log.d("EventActivity", "移动!!!!!!!!!!!");
}else if(action==MotionEvent.ACTION_UP){//抬起
Log.d("EventActivity", "抬起!!!!!!!!!!!");
}
return false;
}
});
打印结果
由此可见button既有触摸事件又有点击事件时
onTouch事件先执行 onClick后执行
原因是什么呢
我们知道 Button ,TextView 等控件的基类都是View,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类(实际上是基类View)里的dispatchTouchEvent方法,所以接下来看View源码中dispatchTouchEvent()方法的具体实现:
分析上述代码,第2行 如果三个条件都为真的话,就返回true,否则执行onTouchEvent,先看第一个条件mOnTouchListener!=null,这个条件就是如果设置了OnTouchListener就会为true,否则是false; 第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true;第三个条件就比较复杂了,mOnTouchListener.onTouch(this, event),这个其实就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。onTouchEvent(MotionEvent event)方法同样也是在view中定义的一个方法,主要是处理传递到view 的手势事件,包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL四种事件。
接下来我们结合上面的具体例子,来分析一下这个过程,首先会执行dispatchTouchEvent(MotionEvent event) ,所以onTouch方法肯定是早于onClick方法的,
如果在onTouch里返回false,就会出现下面的现象:
即先执行了onTouch,再执行了onClick事件
如果onTouch里返回true,则出现下面的现象:
只执行了onTouch,没有执行onClick
原因是如果onTouch返回true的话,则dispatchEvent(MotionEvent event)方法直接返回true了,相当于不往下传递事件了,所以onClick不会执行,相反如果onTouch返回false的话(此时会执行onClick方法),则会执行 onTouchEvent(MotionEvent event)方法,由此可以得出这样一个结论,onClick事件的具体调用执行肯定是在onTouchEvent(MotionEvent event)方法源码中,接下来分析一下该函数的源码:
- public boolean onTouchEvent(MotionEvent event) {
- final int viewFlags = mViewFlags;
- if ((viewFlags & ENABLED_MASK) == DISABLED) {
- // A disabled view that is clickable still consumes the touch
- // events, it just doesn't respond to them.
- return (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
- }
- if (mTouchDelegate != null) {
- if (mTouchDelegate.onTouchEvent(event)) {
- return true;
- }
- }
- if (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
- switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
- if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
- // take focus if we don't have it already and we should in
- // touch mode.
- boolean focusTaken = false;
- if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
- focusTaken = requestFocus();
- }
- if (!mHasPerformedLongPress) {
- // This is a tap, so remove the longpress check
- removeLongPressCallback();
- // Only perform take click actions if we were in the pressed state
- if (!focusTaken) {
- // Use a Runnable and post this rather than calling
- // performClick directly. This lets other visual state
- // of the view update before click actions start.
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {
- performClick();
- }
- }
- }
- if (mUnsetPressedState == null) {
- mUnsetPressedState = new UnsetPressedState();
- }
- if (prepressed) {
- mPrivateFlags |= PRESSED;
- refreshDrawableState();
- postDelayed(mUnsetPressedState,
- ViewConfiguration.getPressedStateDuration());
- } else if (!post(mUnsetPressedState)) {
- // If the post failed, unpress right now
- mUnsetPressedState.run();
- }
- removeTapCallback();
- }
- break;
- case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
- mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- break;
- case MotionEvent.ACTION_CANCEL:
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- removeTapCallback();
- break;
- case MotionEvent.ACTION_MOVE:
- final int x = (int) event.getX();
- final int y = (int) event.getY();
- // Be lenient about moving outside of buttons
- int slop = mTouchSlop;
- if ((x < 0 - slop) || (x >= getWidth() + slop) ||
- (y < 0 - slop) || (y >= getHeight() + slop)) {
- // Outside button
- removeTapCallback();
- if ((mPrivateFlags & PRESSED) != 0) {
- // Remove any future long press/tap checks
- removeLongPressCallback();
- // Need to switch from pressed to not pressed
- mPrivateFlags &= ~PRESSED;
- refreshDrawableState();
- }
- }
- break;
- }
- return true;
- }
- return false;
- }
虽然源码有点多,但是我们只重点关注关键代码,在38行我们看到了代码:performClick();这个方法从名字表义来看就是OnClick方法的调用,我们进入到该方法中去看一探究竟,是否执行了OnClick方法呢?
- public boolean performClick() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
- if (mOnClickListener != null) {
- playSoundEffect(SoundEffectConstants.CLICK);
- mOnClickListener.onClick(this);
- return true;
- }
- return false;
- }
从上述代码可以看到,只要mOnClickListener不是null,就会去调用它的onClick方法,那mOnClickListener又是在哪里赋值的呢?经过分析后找到如下方法:
- public void setOnClickListener(OnClickListener l) {
- if (!isClickable()) {
- setClickable(true);
- }
- mOnClickListener = l;
- }
而上述这个方法就是我们在Application层经常使用的方法,即我们给button 设置点击事件的时候就会调用该方法了,分析到这了,我们知道了OnClick方法确实是在OnTouchEvent方法中,那么除了要设置 OnClickListener,调用onClick的条件又是什么呢?我们从38行代码往前推,从第14行可以分析出:
只要该控件是可点击的或者是长按类型的,则会进入到MotionEvent.ACTION_UP这个分支当中 ,然后经过各种条件判断,则会进入到38行的performClick()方法中。
至此,一切都清晰明白了!当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值。然后每当控件被点击时或者长按时,都会在performClick()方法里回调被点击控件的onClick方法。
关于OnTouchEvent(MotionEvent事件)事件的层级传递。我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
一个简单的例子 希望对你理解Android事件传递机制的理解有所帮助!
- Android事件传递机制(一)
- Android View事件传递机制(一)
- Android事件传递机制(一)
- Android事件传递机制(一)
- Android Touch事件传递机制解析(一)
- Android事件传递机制(一)deprecated
- Android View触摸事件传递机制 一
- Android事件分发(事件传递机制)
- android 事件传递机制
- android 事件传递机制
- android事件传递机制
- Android事件传递机制
- Android 事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- Android事件传递机制
- 蓝牙广播包
- fragment的onActivityResult没有调用的解决方法
- iframe监听鼠标点击事件
- js屏蔽浏览器(IE和FireFox)的刷新功能
- windows下简单的IOCP模型迭代回声服务器实例
- Android事件传递机制(一)
- Mac版office软件包(破解版)
- 用函数指针变量做函数的参数
- virtual memory exhausted: Cannot allocate memory
- 页面跳转方法
- web架构延变
- js性能优化之分时函数
- Java访问控制权限
- mybatis详细学习日志