Android GUI之View事件处理
2015-10-08 14:29
573 查看
Android中的事件分为按键事件和触屏事件,本篇文章将分析View是如何处理Touch事件的。在View中定义了许多触屏事件,比如OnClick、OnLongClick等等,这些事件都是由一次Touch中的动作如ACTION_DOWN、ACTION_MOVE、ACTION_UP组成的。
首先,我们先看一个简单的案例,Activity中只含有一个Button,我们为Button分别绑定了OnClickListener和OnTouchListener,具体代码如下:
那么,当我们点击按钮时,到底先执行哪个事件呢?具体执行结果如下:
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090315381-1676540352.png)
很明显,先执行了OnTouch事件,然后才执行了OnClick,这是为什么呢?我们下面跟踪源码,具体查看下整个事件的执行流程。
当我们点击Button按钮时,首先将要执行的是dispatchTouchEvent方法,至于为什么会执行此方法,我们后面再进行分析。通过源码,我们并没有在Button中找到该方法,顺着Button的继承体系,我们追踪到View,发现该方法存在于View中,具体内容如下:
在此段代码中,涉及到了onTouchEvent方法和onTouchListener,此二者都是View中用于处理Touch事件。很显然,如果View控件设置了onTouchListener监听器,并且当前View控件是可用的,则先执行onTouchListener中的onTouch方法,如果此方法返回了false,则继续执行View的onTouchEvent方法,如果返回true,则到此为止,不在执行onTouchEvent方法。简单验证一下,我们将上面的案例稍微改造下,我们自定义一个MyButton,让其继承Button,并重写onTouchEvent方法,在其中加入一条输入语句,并将MyButton替换掉Button,具体如下:
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090734881-70436440.png)
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090812569-1572875683.png)
在这里很明显可以看到,最终调用了onClickListener监听器。我们可以总结一下,在View的Touch事件处理中,首先会调用View中的dispatchTouchEvent方法,disptchTouchEvent将事件传递给onTouchListener和onTouchEvent方法进行处理,其中onTouchListener要优先于onTouchEvent,如果onTouchListener的返回值为true则不再执行onTouchEvent。在onTouchEvent方法中,根据Touch的动作分别处理相关的事件,如onClick、onLongClick等,最终执行完成后返回dispatchTouchEvent方法。
想要了解更多内容的小伙伴,可以点击查看源码,亲自运行测试。
疑问咨询或技术交流,请加入官方QQ群:
![](http://pub.idqqimg.com/wpa/images/group.png)
(452379712)
作者:杰瑞教育
出处:http://www.cnblogs.com/jerehedu/
本文版权归烟台杰瑞教育科技有限公司和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
首先,我们先看一个简单的案例,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; } });
那么,当我们点击按钮时,到底先执行哪个事件呢?具体执行结果如下:
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090315381-1676540352.png)
很明显,先执行了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); }
点击按钮,输出结果为:
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090734881-70436440.png)
将onTouchListener中的onTouch方法中的返回值改为true,其结果为:
![](http://images2015.cnblogs.com/blog/708076/201509/708076-20150923090812569-1572875683.png)
很明显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群:
![](http://pub.idqqimg.com/wpa/images/group.png)
(452379712)
作者:杰瑞教育
出处:http://www.cnblogs.com/jerehedu/
本文版权归烟台杰瑞教育科技有限公司和CSDN共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章推荐
- iOS中的动画:核心动画Core Animation, UIView动画, Block动画, UIImageView的帧动画.
- MUI - H5实现ios长按图标后进入图标排序及删除功能的效果
- UITableViewCell高度自适应Label高度
- 使用Anemometer基于pt-query-digest将MySQL慢查询可视化
- Codeforces Round #216 (Div. 2) E. Valera and Queries 树状数组 离线处理
- Android GUI之Window、WindowManager
- UI_动画-UIView属性动画、block块、仿设变换、CAAnimation
- UIGestureRecognizer手势
- POJ 2533 Longest Ordered Subsequence (LIS)
- 使用UINib加载xib文件实现UITableViewCell
- 1007. Maximum Subsequence Sum (25)
- SparkSQL05_prepareForExecution_01_EnsureRequirements
- 【转载】今天心情非常好,再发一组 Linq、 集合、数组、Lambda、QuerySyntax 的文章
- UIActionSheet的最后一项点击失效
- 修改IOS中UISearchBar的取消按钮及所有视图看了其他的文章都不试一下就发出来,更本就存在些纰漏,下面是我做了稍许修改的结果:
- easyUI日历
- POJ 3566 Building for UN (无脑构造 水题)
- Integer.toString()与String.valueOf(Oject)有什么不同(2015年10月8日)
- Visual Studio: whether auto-building when press the debug button
- Android中startActivity中的permission检测与UID机制