300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Android 触摸事件机制(三) View中触摸事件详解

Android 触摸事件机制(三) View中触摸事件详解

时间:2021-07-09 15:57:54

相关推荐

Android 触摸事件机制(三) View中触摸事件详解

本文将对View中触摸事件相关的内容进行介绍。重点介绍的是dispatchTouchEvent(), onTouchEvent()这两个API以及OnTouchListener接口。

注意:本文是基于Android 4.4.2版本进行介绍的!

目录

1.View中触摸事件的概述

2.View中触摸事件的源码解析

2.1.View中的dispatchTouchEvent

2.2.View中的onTouchEvent

1. View中触摸事件的概述

View中与触摸事件相关的内容可以分为两部分。

第一部分dispatchTouchEvent()和onTouchEvent()这两个API

dispatchTouchEvent()是传递触摸事件的API,而onTouchEvent()则是View处理触摸事件的API。

View中dispatchTouchEvent()将事件传递给"自己的onTouch()", "自己的onTouchEvent()"进行处理。 onTouch()是OnTouchListener接口中API,属于View提供的,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统提供的,用于处理触摸事件的接口;在onTouchEvent()中会进行一系列的动作,例如获取焦点、设置按下状态,调用onClick()等。

第二部分OnTouchListener, OnClickListener, OnLongClickListener等接口

这部分主要是接口。但本文主要介绍的是OnTouchListener接口中的onTouch()。为什么呢?

这是因为,onTouch()与onTouchEvent()都是用户处理触摸事件的API。

但不同的是:(01), onTouch()是View提供给用户,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统自己实现的接口。(02),onTouch()的优先级比onTouchEvent()的优先级更高。dispatchTouchEvent()中分发事件的时候,会先将事件分配给onTouch()进行处理,然后才分配给onTouchEvent()进行处理。 如果onTouch()对触摸事件进行了处理,并且返回true;那么,该触摸事件就不会分配在分配给onTouchEvent()进行处理了。只有当onTouch()没有处理,或者处理了但返回false时,才会分配给onTouchEvent()进行处理。

public interface OnTouchListener {boolean onTouch(View v, MotionEvent event);}

2. View中触摸事件的源码解析

2.1 View中的dispatchTouchEvent

public boolean dispatchTouchEvent(MotionEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}// 如果该View被遮蔽,并且该View在被遮蔽时不响应点击事件;// 此时,返回false;不会执行onTouch()或onTouchEvent(),即过滤调用该点击事件。// 否则,返回true。// 被遮蔽的意思是:该View不是位于顶部,有其他的View在它之上。if (onFilterTouchEventForSecurity(event)) {//noinspection SimplifiableIfStatementListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {return true;}if (onTouchEvent(event)) {return true;}}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}return false;}

说明:该代码定义在frameworks/base/core/java/android/view/View.java中。

(01) mInputEventConsistencyVerifier是调试用的,这里不用理会。重点看onFilterTouchEventForSecurity()部分。

(02) onFilterTouchEventForSecurity()表示是否要分发该触摸事件;如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即不会执行onTouch()与onTouchEvent()。 否则的话,则将事件分发给onTouch(), onTouchEvent()进行处理。 (03) 如果将事件进行分发的话,会先尝试分发给onTouch();然后才分发给onTouchEvent()。

2.1.1 View中的onFilterTouchEventForSecurity()

public boolean onFilterTouchEventForSecurity(MotionEvent event) {//noinspection RedundantIfStatementif ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {// Window is obscured, drop this touch.return false;}return true;}

说明:onFilterTouchEventForSecurity()返回true,表示可以分发该触摸事件;否则,不能分发该触摸事件。不能分发事件的情况,只有mViewFlags&FILTER_TOUCHES_WHEN_OBSCURED!=0,并且event.getFlags()&MotionEvent.FLAG_WINDOW_IS_OBSCURED!=0同时成立。

(01) FILTER_TOUCHES_WHEN_OBSCURED是android:filterTouchesWhenObscured属性所对应的位。android:filterTouchesWhenObscured是true的话,则表示其他视图在该视图之上,导致该视图被隐藏时,该视图就不再响应触摸事件。

(02) MotionEvent.FLAG_WINDOW_IS_OBSCURED为true的话,则表示该视图的窗口是被隐藏的。

2.2 View中的onTouchEvent

public boolean onTouchEvent(MotionEvent event) {final int viewFlags = mViewFlags;// 如果View被禁用的话,则返回它是否可以点击。if ((viewFlags & ENABLED_MASK) == DISABLED) {if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {setPressed(false);}return (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));}// 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。// mTouchDelegate的默认值是null。if (mTouchDelegate != null) {if (mTouchDelegate.onTouchEvent(event)) {return true;}}// 如果View可以被点击的话,则执行if里面的内容。// 这其中涉及到的主要是获取焦点,设置按下状态,触发onClick(), onLongClick()事件等等。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);}if (!mHasPerformedLongPress) {// This is a tap, so remove the longpress checkremoveLongPressCallback();// Only perform take click actions if we were in the pressed stateif (!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 nowmUnsetPressedState.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();}postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());} else {// Not inside a scrolling container, so show the feedback right awaysetPressed(true);checkForLongClick(0);}break;case MotionEvent.ACTION_CANCEL:setPressed(false);removeTapCallback();removeLongPressCallback();break;case MotionEvent.ACTION_MOVE:final int x = (int) event.getX();final int y = (int) event.getY();// Be lenient about moving outside of buttonsif (!pointInView(x, y, mTouchSlop)) {// Outside buttonremoveTapCallback();if ((mPrivateFlags & PFLAG_PRESSED) != 0) {// Remove any future long press/tap checksremoveLongPressCallback();setPressed(false);}}break;}return true;}return false;}

说明:onTouchEvent()是Android系统实现的View对触摸事件的处理。

(01) 如果View被禁用的话,则返回它是否可以点击。当我们调用了setEnabled(false)时,View就被禁用了;默认情况下,View是可用的。当调用setClickable(true)或者android:clickable为true时,View就是可点击状态;默认情况下,View是不可点击的。

(02) 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。例如,假设有两个视图v1和v2,它们的布局相互之间不重叠;如果设置了v1.setTouchDelegate(v2)的话,v1的触摸事件就会分发给v2。 注意:mTouchDelegate的默认值是null。

(03) 如果View可以被点击的话,则执行if里面的内容。if里面涉及的内容很多,这里与本文的主题关联不大,暂且不表;如果要细将的话,估计得好几篇文章。例如,setPressed()是设置View的按下状态,如果用户有设置View在不同状态的图片时,setPressed()时会导致View的图片的更新。

View中关于触摸事件的代码就分析至此。总的来说:

(01)View中的dispatchTouchEvent()会将事件传递给"自己的onTouch()", "自己的onTouchEvent()"进行处理。而且onTouch()的优先级比onTouchEvent()的优先级要高。(02)onTouch()与onTouchEvent()都是View中用户处理触摸事件的API。onTouch是OnTouchListener接口中的函数,OnTouchListener接口需要用户自己实现。onTouchEvent()是View自带的接口,Android系统提供了默认的实现;当然,用户可以重载该API。

(03)onTouch()与onTouchEvent()有两个不同之处:(01), onTouch()是View提供给用户,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统自己实现的接口。(02),onTouch()的优先级比onTouchEvent()的优先级更高。dispatchTouchEvent()中分发事件的时候,会先将事件分配给onTouch()进行处理,然后才分配给onTouchEvent()进行处理。 如果onTouch()对触摸事件进行了处理,并且返回true;那么,该触摸事件就不会分配在分配给onTouchEvent()进行处理了。只有当onTouch()没有处理,或者处理了但返回false时,才会分配给onTouchEvent()进行处理。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。