首页 技术 正文
技术 2022年11月23日
0 收藏 620 点赞 2,786 浏览 6261 个字

   Android中的事件分为按键事件和触屏事件,本篇文章将分析View是如何处理Touch事件的。在View中定义了许多触屏事件,比如OnClick、OnLongClick等等,这些事件都是由一次Touch中的动作如ACTION_DOWN、ACTION_MOVE、ACTION_UP组成的。

  首先,我们先看一个简单的案例,Activity中只含有一个Button,我们为Button分别绑定了OnClickListener和OnTouchListener,具体代码如下:

btnOk.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG,"按钮的点击事件执行了");
}
});btnOk.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.i(TAG,"按钮的Touch事件执行了");
return false;
}
});

那么,当我们点击按钮时,到底先执行哪个事件呢?具体执行结果如下:

Android GUI之View事件处理

  很明显,先执行了OnTouch事件,然后才执行了OnClick,这是为什么呢?我们下面跟踪源码,具体查看下整个事件的执行流程。

  当我们点击Button按钮时,首先将要执行的是dispatchTouchEvent方法,至于为什么会执行此方法,我们后面再进行分析。通过源码,我们并没有在Button中找到该方法,顺着Button的继承体系,我们追踪到View,发现该方法存在于View中,具体内容如下:

public boolean dispatchTouchEvent(MotionEvent event) {
// If the event should be handled by accessibility focus first.
if (event.isTargetAccessibilityFocus()) {
// We don't have focus or no virtual descendant has it, do not handle the event.
if (!isAccessibilityFocusedViewOrHost()) {
return false;
}
// We have focus and got the event, then use normal event dispatch.
event.setTargetAccessibilityFocus(false);
} boolean result = false; if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
} final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Defensive cleanup for new gesture
stopNestedScroll();
} if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
} if (!result && onTouchEvent(event)) {
result = true;
}
} if (!result && mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
} // Clean up after nested scrolls if this is the end of a gesture;
// also cancel it if we tried an ACTION_DOWN but we didn't want the rest
// of the gesture.
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
} return result;
}
仔细分析阅读源码,我们发现了关键的处理部分,具体如下:
if (onFilterTouchEventForSecurity(event)) {
//noinspection SimplifiableIfStatement
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
} if (!result && onTouchEvent(event)) {
result = true;
}
}

  在此段代码中,涉及到了onTouchEvent方法和onTouchListener,此二者都是View中用于处理Touch事件。很显然,如果View控件设置了onTouchListener监听器,并且当前View控件是可用的,则先执行onTouchListener中的onTouch方法,如果此方法返回了false,则继续执行View的onTouchEvent方法,如果返回true,则到此为止,不在执行onTouchEvent方法。简单验证一下,我们将上面的案例稍微改造下,我们自定义一个MyButton,让其继承Button,并重写onTouchEvent方法,在其中加入一条输入语句,并将MyButton替换掉Button,具体如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("View事件处理","这是View的onTouchEvent" +
"方法,当前动作为:"+event.getAction());
return super.onTouchEvent(event);
}
点击按钮,输出结果为:

Android GUI之View事件处理

将onTouchListener中的onTouch方法中的返回值改为true,其结果为:

Android GUI之View事件处理

很明显MyButton中的onTouch方法及onClick都没有执行。
从而也可以知道onClick时间肯定和onTouch方法有关系,查看View中的onTouch方法,具体源码如下:
public boolean onTouchEvent(MotionEvent event) {
……
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_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 (prepressed) {
// The button is being released before we actually
// showed it as pressed. Make it show the pressed
// state now (before scheduling the click) to ensure
// the user sees it.
setPressed(true, x, y);
} 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) {
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
} removeTapCallback();
}
break; case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false; if (performButtonActionOnTouchDown(event)) {
break;
} // Walk up the hierarchy to determine if we're inside a scrolling container.
boolean isInScrollingContainer = isInScrollingContainer(); // For views inside a scrolling container, delay the pressed feedback for
// a short period in case this is a scroll.
if (isInScrollingContainer) {
mPrivateFlags |= PFLAG_PREPRESSED;
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPendingCheckForTap.x = event.getX();
mPendingCheckForTap.y = event.getY();
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
} else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true, x, y);
checkForLongClick(0);
}
break; case MotionEvent.ACTION_CANCEL:
setPressed(false);
removeTapCallback();
removeLongPressCallback();
break; case MotionEvent.ACTION_MOVE:
drawableHotspotChanged(x, y); // Be lenient about moving outside of buttons
if (!pointInView(x, y, mTouchSlop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback(); setPressed(false);
}
}
break;
} return true;
} return false;
}
在case MotionEvent.ACTION_UP:
的分支中我们发现了performClick方法,该方法就是单击事件处理方法,可查看源码如下:
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
} sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}

  在这里很明显可以看到,最终调用了onClickListener监听器。我们可以总结一下,在View的Touch事件处理中,首先会调用View中的dispatchTouchEvent方法,disptchTouchEvent将事件传递给onTouchListener和onTouchEvent方法进行处理,其中onTouchListener要优先于onTouchEvent,如果onTouchListener的返回值为true则不再执行onTouchEvent。在onTouchEvent方法中,根据Touch的动作分别处理相关的事件,如onClick、onLongClick等,最终执行完成后返回dispatchTouchEvent方法。

  想要了解更多内容的小伙伴,可以点击查看源码,亲自运行测试。

  疑问咨询或技术交流,请加入官方QQ群:Android GUI之View事件处理 (452379712)

作者:杰瑞教育
出处:http://www.cnblogs.com/jerehedu/ 
本文版权归烟台杰瑞教育科技有限公司和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 

微信扫一扫

支付宝扫一扫

本文网址:https://www.zhankr.net/141446.html

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:876 阅读:5,475
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:806 阅读:3,696
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:565 阅读:4,510
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:731 阅读:4,472
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:5,388
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:3,268
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:8:00-16:00

客服电话

400-888-8888

客服邮箱

ceotheme@ceo.com

扫描二维码

关注微信公众号

扫描二维码

手机访问本站