有一个Button 按钮,要想为该按钮设置onClick事件和OnTouch事件
mTestButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d(TAG, "onClick execute");
}
});
mTestButton.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Log.d(TAG, "onTouch execute, action event " + motionEvent.getAction());
return false;
}
});
此时,我们现在分析一下,是onTouch先执行,还是onClick执行,接下来我从FrameWork 源码去探寻一下整个事件的执行流程和原理:
我们知道 Button ,TextView 等基础控件的基类都是View,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类(实际上是基类View)里的dispatchTouchEvent方法,所以接下来看View源码中dispatchTouchEvent()方法的具体实现:
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
分析上述代码,第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,就会出现下面的现象:
10-20 18:57:49.670: DEBUG/MainActivity(20153): onTouch execute, action event 0
10-20 18:57:49.715: DEBUG/MainActivity(20153): onTouch execute, action event 1
10-20 18:57:49.715: DEBUG/MainActivity(20153): onClick execute
即先执行了onTouch,再执行了onClick事件,而且onTouch执行了两次,一个是action_down,一个是action_up事件;
如果onTouch里返回true,则出现下面的现象:
10-20 19:01:59.795: DEBUG/MainActivity(21010): onTouch execute, action event 0
10-20 19:01:59.860: DEBUG/MainActivity(21010): onTouch execute, action event 1
结果是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。
那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。
以上这篇浅谈onTouch先执行,还是onClick执行(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# ontouch
# onclick
# 解决vue的touchStart事件及click事件冲突问题
# JS中touchstart事件与click事件冲突的解决方法
# 详谈Android中onTouch与onClick事件的关系(必看)
# vue中touch和click共存的解决方式
# 移动端touch拖动和click事件冲突问题解决
# 就会
# 是在
# 都是
# 则会
# 又是
# 给大家
# 当我们
# 会去
# 回调
# 第一个
# 的说
# 就不
# 则是
# 才会
# 你在
# 一切都
# 两次
# 会在
# 经验之谈
# 我们可以
相关文章:
湖南网站制作公司,湖南上善若水科技有限公司做什么的?
专业网站制作企业网站,如何制作一个企业网站,建设网站的基本步骤有哪些?
网站好制作吗知乎,网站开发好学吗?有什么技巧?
深圳网站制作公司好吗,在深圳找工作哪个网站最好啊?
公司网站设计制作厂家,怎么创建自己的一个网站?
广州顶尖建站服务:企业官网建设与SEO优化一体化方案
浅析上传头像示例及其注意事项
历史网站制作软件,华为如何找回被删除的网站?
,如何利用word制作宣传手册?
如何在宝塔面板创建新站点?
南宁网站建设制作定制,南宁网站建设可以定制吗?
网站制作公司排行榜,抖音怎样做个人官方网站
已有域名和空间如何快速搭建网站?
图片制作网站免费软件,有没有免费的网站或软件可以将图片批量转为A4大小的pdf?
如何挑选最适合建站的高性能VPS主机?
宝华建站服务条款解析:五站合一功能与SEO优化设置指南
交易网站制作流程,我想开通一个网站,注册一个交易网址,需要那些手续?
如何在服务器上三步完成建站并提升流量?
如何正确选择百度移动适配建站域名?
建站之星手机一键生成:多端自适应+小程序开发快速建站指南
个人摄影网站制作流程,摄影爱好者都去什么网站?
建站之星如何实现五合一智能建站与营销推广?
如何自己制作一个网站链接,如何制作一个企业网站,建设网站的基本步骤有哪些?
导航网站建站方案与优化指南:一站式高效搭建技巧解析
如何通过.red域名打造高辨识度品牌网站?
常州企业网站制作公司,全国继续教育网怎么登录?
如何零基础在云服务器搭建WordPress站点?
如何选择高效便捷的WAP商城建站系统?
如何用已有域名快速搭建网站?
北京网站制作的公司有哪些,北京白云观官方网站?
大学网站设计制作软件有哪些,如何将网站制作成自己app?
网站制作公司哪里好做,成都网站制作公司哪家做得比较好,更正规?
建站主机是什么?如何选择适合的建站主机?
专业商城网站制作公司有哪些,pi商城官网是哪个?
建站主机选购指南与交易推荐:核心配置解析
如何快速使用云服务器搭建个人网站?
长沙做网站要多少钱,长沙国安网络怎么样?
音响网站制作视频教程,隆霸音响官方网站?
佛山企业网站制作公司有哪些,沟通100网上服务官网?
如何规划企业建站流程的关键步骤?
成都品牌网站制作公司,成都营业执照年报网上怎么办理?
如何通过FTP空间快速搭建安全高效网站?
如何配置支付宝与微信支付功能?
沈阳制作网站公司排名,沈阳装饰协会官方网站?
重庆网站制作公司哪家好,重庆中考招生办官方网站?
如何配置WinSCP新建站点的密钥验证步骤?
文字头像制作网站推荐软件,醒图能自动配文字吗?
如何在宝塔面板中修改默认建站目录?
高性能网站服务器部署指南:稳定运行与安全配置优化方案
专业的网站制作设计是什么,如何制作一个企业网站,建设网站的基本步骤有哪些?
*请认真填写需求信息,我们会在24小时内与您取得联系。