Android 中的Touch事件分发机制
来源:互联网 发布:股票历史分时数据获取 编辑:程序博客网 时间:2024/05/18 03:00
写这篇博客的主要用意,是自己mark下。
由来:1.ScrollView+EditText结合使用时出现触摸事件异常
2.自定义控件使用时,touch处理不对
3.面试问到
事件分发主要分为两部分:view的事件分发和viewgroup的事件分发。在探讨事件分发机制之前,先需要搞清楚android两个基础控件view和viewgroup,以及它们之间的关系:view是没有子控件的,像button,textview都是view控件。而viewgroup继承自view,是可以存在子控件的。也就是说viewgroup就是一组view或者是viewroup的集合,它是所有页面布局的父类(eg linearlayout,relativelayout)
1.view的事件分发: dsipatchTouchEvent 方法:事件分发 和 onTouchEvent方法:事件消费
下面例子用来模拟View事件处理流程
所以定义了CustomButton类,重写了dispatchTouchEvent方法和onTouchEvent方法
<span style="font-size:14px;">public class CustomButton extends Button {private static final String TAG = "MainActivity";public CustomButton(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stub}public CustomButton(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}public CustomButton(Context context) {super(context);// TODO Auto-generated constructor stub} @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG, "MainActivity-CustomButton-onTouchEvent-ACTION_DOWN"); break; case MotionEvent.ACTION_UP: Log.i(TAG, "MainActivity-CustomButton-onTouchEvent-ACTION_UP"); break; default: break; } return super.onTouchEvent(event); } // 位于view里面 @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i(TAG, "MainActivity-CustomButton-dispatchTouchEvent-ACTION_DOWN"); break; case MotionEvent.ACTION_UP: Log.i(TAG, "MainActivity-CustomButton-dispatchTouchEvent-ACTION_UP"); break; default: break; } return super.dispatchTouchEvent(event); } }</span>
2.MainActivity中对Button的onClick事件进行监听,重写Activity中的dispatchTouchEvent()方法和onTouchEvent()方法
public class MainActivity extends Activity implements OnClickListener {private CustomButton mViewBtn;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.main);mViewBtn = (CustomButton) findViewById(R.id.view_btn);mViewBtn.setOnClickListener(this);mViewBtn.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: Log.i("MainActivity", "CustomButton-onTouch-ACTION_DOWN"); break; case MotionEvent.ACTION_UP: Log.i("MainActivity", "CustomButton-onTouch-ACTION_UP"); break; default: break; } return false; } }); }@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubint action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "MainActivity-dispatchTouchEvent action = action down");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "MainActivity-dispatchTouchEvent action = action up");break;default:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "MainActivity-onTouchEvent action = action down");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "MainActivity-onTouchEvent action = action up");break;default:break;}return super.onTouchEvent(event);}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint id = v.getId();switch (id) {case R.id.view_btn:Log.i("MainActivity", "MainActivity view_btn clicked");break;default:break;}}}
点击Button输出结果如下:
03-23 06:21:36.549: I/MainActivity(2056): MainActivity-dispatchTouchEvent action = action down03-23 06:21:36.549: I/MainActivity(2056): MainActivity-CustomButton-dispatchTouchEvent-ACTION_DOWN03-23 06:21:36.549: I/MainActivity(2056): CustomButton-onTouch-ACTION_DOWN03-23 06:21:36.549: I/MainActivity(2056): MainActivity-CustomButton-onTouchEvent-ACTION_DOWN03-23 06:21:36.677: I/MainActivity(2056): MainActivity-dispatchTouchEvent action = action up03-23 06:21:36.677: I/MainActivity(2056): MainActivity-CustomButton-dispatchTouchEvent-ACTION_UP03-23 06:21:36.677: I/MainActivity(2056): CustomButton-onTouch-ACTION_UP03-23 06:21:36.677: I/MainActivity(2056): MainActivity-CustomButton-onTouchEvent-ACTION_UP03-23 06:21:36.687: I/MainActivity(2056): MainActivity view_btn clicked
从上述的结果上,我们可以看出,touch down事件,首先由的Activity中dispatchTouchEvetn方法进行事件分发,然后传递给CustomButton中的dispatchTouchEvent进行事件分发,然后传递给onTouchEvent方法对touch事件处理,up事件处理流程同down事件
流程,最后执行button中的onclick方法。
通过查看View类中的dispatchTouchEvent和onTouchEvent源码
public boolean dispatchTouchEvent(MotionEvent event) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(event, 0); } if (onFilterTouchEventForSecurity(event)) { //noinspection SimplifiableIfStatement ListenerInfo 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; }li != null && li.mOnTouchListener && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)如果这三个条件都为真,就返回true,否则的话就去执行onTouchEvent(event)并返回结果,如果都不合适就返回false。
第一个条件:li != null && li.mOnTouchListener ,只要控件注册了onTouchListener监听,就不能为null
第二个条件: (mViewFlags & ENABLED_MASK) == ENABLED ,一般控件的属性enabled都是true,所以成立
第三个条件:li.mOnTouchListener.onTouch(this, event),这里是回调touch注册事件里面的onTouch方法,如果onTouch返回true则dsipatchTouchEvent 也会返回true;如果onTouch方法返回false,则会再去执行onTouchEvent方法。
这里可以看出dsipatchTouchEvent 中onTouch和onTouchEvent方法都会被执行,最先执行的是onTouch,所以也就解释了为什么打印出来的log中onTouch比onclick要先出现咯,现在也仅仅是解释了这个。既然oclick事件被执行了,那么就肯定是在onTouchEvent方法里面被执行的。
public boolean onTouchEvent(MotionEvent event) { final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) { setPressed(false); } // 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 & 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 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(); } postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away setPressed(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 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; }
上述代码可以看到up事件后调用了performClick方法
public boolean performClick() { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); return true; } return false; }只要li != null && li.mOnClickListener != null条件成立,就进行onClick事件处理,现在整个流程就通了。
总结,touch事件入口是dispatchTouchEvent,首先执行注册的onTouch监听,如果返回的结果是false,就会接着执行onTouchEvent。在执行onTouchEvent的时候,会执行onClick监听,这样就解释清楚了打印的顺序。在dsipatchTouchEvent 进行事件分发的时候,一旦返回true,就表示该事件已经被消费了处理了,不会再继续往下传了。如果onTouch返回true,那么它dsipatchTouchEvent 就会返回true,表示事件已被处理。不会去执行onTouchEvent了,也就更不会去处理onclick监听了。这里我们改一下上面的例子,看下打印出来的结果哈:
03-23 06:20:42.499: I/MainActivity(2006): MainActivity-dispatchTouchEvent action = action down03-23 06:20:42.499: I/MainActivity(2006): MainActivity-CustomButton-dispatchTouchEvent-ACTION_DOWN03-23 06:20:42.499: I/MainActivity(2006): CustomButton-onTouch-ACTION_DOWN03-23 06:20:42.541: I/MainActivity(2006): MainActivity-dispatchTouchEvent action = action up03-23 06:20:42.541: I/MainActivity(2006): MainActivity-CustomButton-dispatchTouchEvent-ACTION_UP03-23 06:20:42.541: I/MainActivity(2006): CustomButton-onTouch-ACTION_UP
这样onTouchEvent就没有执行了。
文章学习参考了:http://blog.csdn.net/guolin_blog/article/details/9097463
2.viewGroup的事件分发: dsipatchTouchEvent 方法:事件分发 和 onTouchEvent方法:事件消费 onInterceptTouchevent:事件拦截
相比较view而言,多了一个onInterceptTouchevent函数,那么它的作用是什么勒,看过另一篇文章,觉得它的比喻比较恰当和形象:onInterceptTouchevent就相当于viewgroup的一个管家,家庭秘书。为什么view没有呢,因为它没有子控件,用不着啊。一个touch事件来了,onInterceptTouchevent函数就负责判断决定是viewgroup自己消费处理呢,还是传递给它的孩子进行消费处理。
首先,自定义Layout
public class CustomLayout extends LinearLayout {private final static String TAG = "MainActivity";public CustomLayout(Context context, AttributeSet attrs) {super(context, attrs);// TODO Auto-generated constructor stub}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "CustomLayout-onTouchEvent-ACTION_DOWN");break;case MotionEvent.ACTION_UP:Log.i(TAG, "CustomLayout-onTouchEvent-ACTION_UP");break;default:break;}return super.onTouchEvent(event);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "CustomLayout-dispatchTouchEvent-ACTION_DOWN");break;case MotionEvent.ACTION_UP:Log.i(TAG, "CustomLayout-dispatchTouchEvent-ACTION_UP");break;default:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:Log.i(TAG, "CustomLayout-onInterceptTouchEvent-ACTION_DOWN");break;case MotionEvent.ACTION_UP:Log.i(TAG, "CustomLayout-onInterceptTouchEvent-ACTION_UP");break;default:break;}return super.onInterceptTouchEvent(ev);}}布局文件
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <com.yjx.touchhandlerdemo.ui.CustomLayout android:id="@+id/linearlayout" android:layout_width="200dp" android:layout_height="200dp" > <com.yjx.touchhandlerdemo.ui.CustomButton android:id="@+id/view_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="150dp" android:text="@string/view" /> </com.yjx.touchhandlerdemo.ui.CustomLayout></RelativeLayout>
然后修改MainActivity,添加自定义layout的click和touch事件:
public class MainActivity extends Activity implements OnClickListener {private CustomButton mViewBtn;private CustomLayout mLayout;@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);setContentView(R.layout.main);mViewBtn = (CustomButton) findViewById(R.id.view_btn);mLayout = (CustomLayout) findViewById(R.id.linearlayout);mViewBtn.setOnClickListener(this);mViewBtn.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "CustomButton-onTouch-ACTION_DOWN");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "CustomButton-onTouch-ACTION_UP");break;default:break;}return false;}});mLayout.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.i("MainActivity", "CustomLayout---onClick");}});mLayout.setOnTouchListener(new View.OnTouchListener() {@Overridepublic boolean onTouch(View v, MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "CustomLayout-onTouch-ACTION_DOWN");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "CustomLayout-onTouch-ACTION_UP");break;default:break;}return false;}});}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubint action = ev.getAction();switch (action) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "MainActivity-dispatchTouchEvent action = action down");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "MainActivity-dispatchTouchEvent action = action up");break;default:break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubint action = event.getAction();switch (action) {case MotionEvent.ACTION_DOWN:Log.i("MainActivity", "MainActivity-onTouchEvent action = action down");break;case MotionEvent.ACTION_UP:Log.i("MainActivity", "MainActivity-onTouchEvent action = action up");break;default:break;}return super.onTouchEvent(event);}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint id = v.getId();switch (id) {case R.id.view_btn:Log.i("MainActivity", "MainActivity view_btn clicked");break;default:break;}}}
点击CustomButton,结果反馈如下:
03-23 06:25:31.962: I/MainActivity(2169): MainActivity-dispatchTouchEvent action = action down03-23 06:25:31.962: I/MainActivity(2169): CustomLayout-dispatchTouchEvent-ACTION_DOWN03-23 06:25:31.962: I/MainActivity(2169): CustomLayout-onInterceptTouchEvent-ACTION_DOWN03-23 06:25:31.962: I/MainActivity(2169): MainActivity-CustomButton-dispatchTouchEvent-ACTION_DOWN03-23 06:25:31.962: I/MainActivity(2169): CustomButton-onTouch-ACTION_DOWN03-23 06:25:31.962: I/MainActivity(2169): MainActivity-CustomButton-onTouchEvent-ACTION_DOWN03-23 06:25:32.021: I/MainActivity(2169): MainActivity-dispatchTouchEvent action = action up03-23 06:25:32.021: I/MainActivity(2169): CustomLayout-dispatchTouchEvent-ACTION_UP03-23 06:25:32.021: I/MainActivity(2169): CustomLayout-onInterceptTouchEvent-ACTION_UP03-23 06:25:32.021: I/MainActivity(2169): MainActivity-CustomButton-dispatchTouchEvent-ACTION_UP03-23 06:25:32.021: I/MainActivity(2169): CustomButton-onTouch-ACTION_UP03-23 06:25:32.021: I/MainActivity(2169): MainActivity-CustomButton-onTouchEvent-ACTION_UP03-23 06:25:32.061: I/MainActivity(2169): MainActivity view_btn clicked
上面的log打印结果显示首先调用viewgroup的dispatchTouchEvent,然后执行viewgroup的onInterceptTouchEvent拦截事件,最后执行的是子控件view的disPatchTouchEvent,剩下就和view的事件分发逻辑是一样的咯,但是为什么会是这样子的呢?
ViewGroup dispatchTouchEvent源码
public boolean dispatchTouchEvent(MotionEvent ev) { if (mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onTouchEvent(ev, 1); } boolean handled = false; if (onFilterTouchEventForSecurity(ev)) { final int action = ev.getAction(); final int actionMasked = action & MotionEvent.ACTION_MASK; // Handle an initial down. if (actionMasked == MotionEvent.ACTION_DOWN) { // Throw away all previous state when starting a new touch gesture. // The framework may have dropped the up or cancel event for the previous gesture // due to an app switch, ANR, or some other state change. cancelAndClearTouchTargets(ev); resetTouchState(); } // Check for interception. final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (!disallowIntercept) { intercepted = onInterceptTouchEvent(ev); ev.setAction(action); // restore action in case it was changed } else { intercepted = false; } } else { // There are no touch targets and this action is not an initial down // so this view group continues to intercept touches. intercepted = true; } // Check for cancelation. final boolean canceled = resetCancelNextUpFlag(this) || actionMasked == MotionEvent.ACTION_CANCEL; // Update list of touch targets for pointer down, if needed. final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0; TouchTarget newTouchTarget = null; boolean alreadyDispatchedToNewTouchTarget = false; if (!canceled && !intercepted) { if (actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { final int actionIndex = ev.getActionIndex(); // always 0 for down final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex) : TouchTarget.ALL_POINTER_IDS; // Clean up earlier touch targets for this pointer id in case they // have become out of sync. removePointersFromTouchTargets(idBitsToAssign); final int childrenCount = mChildrenCount; if (childrenCount != 0) { // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); final boolean customOrder = isChildrenDrawingOrderEnabled(); for (int i = childrenCount - 1; i >= 0; i--) { final int childIndex = customOrder ? getChildDrawingOrder(childrenCount, i) : i; final View child = children[childIndex]; if (!canViewReceivePointerEvents(child) || !isTransformedTouchPointInView(x, y, child, null)) { continue; } newTouchTarget = getTouchTarget(child); if (newTouchTarget != null) { // Child is already receiving touch within its bounds. // Give it the new pointer in addition to the ones it is handling. newTouchTarget.pointerIdBits |= idBitsToAssign; break; } resetCancelNextUpFlag(child); if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) { // Child wants to receive touch within its bounds. mLastTouchDownTime = ev.getDownTime(); mLastTouchDownIndex = childIndex; mLastTouchDownX = ev.getX(); mLastTouchDownY = ev.getY(); newTouchTarget = addTouchTarget(child, idBitsToAssign); alreadyDispatchedToNewTouchTarget = true; break; } } } if (newTouchTarget == null && mFirstTouchTarget != null) { // Did not find a child to receive the event. // Assign the pointer to the least recently added target. newTouchTarget = mFirstTouchTarget; while (newTouchTarget.next != null) { newTouchTarget = newTouchTarget.next; } newTouchTarget.pointerIdBits |= idBitsToAssign; } } } // Dispatch to touch targets. if (mFirstTouchTarget == null) { // No touch targets so treat this as an ordinary view. handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS); } else { // Dispatch to touch targets, excluding the new touch target if we already // dispatched to it. Cancel touch targets if necessary. TouchTarget predecessor = null; TouchTarget target = mFirstTouchTarget; while (target != null) { final TouchTarget next = target.next; if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) { handled = true; } else { final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted; if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) { handled = true; } if (cancelChild) { if (predecessor == null) { mFirstTouchTarget = next; } else { predecessor.next = next; } target.recycle(); target = next; continue; } } predecessor = target; target = next; } } // Update list of touch targets for pointer up or cancel, if needed. if (canceled || actionMasked == MotionEvent.ACTION_UP || actionMasked == MotionEvent.ACTION_HOVER_MOVE) { resetTouchState(); } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) { final int actionIndex = ev.getActionIndex(); final int idBitsToRemove = 1 << ev.getPointerId(actionIndex); removePointersFromTouchTargets(idBitsToRemove); } } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1); } return handled; }通过上面的代码我们可以看到dispatchTransformedTouchEvent方法的调用对childview进行touch事件分发。
onInterceptTouchEvent方法返回true后,点击Button输入结果为
03-23 06:30:07.441: I/MainActivity(2226): MainActivity-dispatchTouchEvent action = action down03-23 06:30:07.441: I/MainActivity(2226): CustomLayout-dispatchTouchEvent-ACTION_DOWN03-23 06:30:07.441: I/MainActivity(2226): CustomLayout-onInterceptTouchEvent-ACTION_DOWN03-23 06:30:07.441: I/MainActivity(2226): CustomLayout-onTouch-ACTION_DOWN03-23 06:30:07.441: I/MainActivity(2226): CustomLayout-onTouchEvent-ACTION_DOWN03-23 06:30:07.487: I/MainActivity(2226): MainActivity-dispatchTouchEvent action = action up03-23 06:30:07.487: I/MainActivity(2226): CustomLayout-dispatchTouchEvent-ACTION_UP03-23 06:30:07.487: I/MainActivity(2226): CustomLayout-onTouch-ACTION_UP03-23 06:30:07.487: I/MainActivity(2226): CustomLayout-onTouchEvent-ACTION_UP03-23 06:30:07.487: I/MainActivity(2226): CustomLayout---onClick上面的结果我们就看出,onInterceptTouchEvent返回true后,ViewGroup把childview的事件拦截,自己消费掉了。
现在整个ViewGroup的事件分发流程的分析也就到此结束了,我们最后再来简单梳理一下吧。
1. Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View的。
2. 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
3. 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。
4.简单来讲,dispatchTouchEvent方法是为了onTouch监听的,onTouchEvent是为了onClick监听的。如果ontouch监听返回false,事件会传递到onTouchEvent当中触发onClick,如果是true的话就不会继续往下传递了。
- Android 中的Touch事件分发机制
- Android Touch事件分发机制
- Android Touch事件分发机制
- android touch事件分发机制
- android Touch事件分发机制
- Android Touch事件分发机制
- Android Touch事件分发机制
- Android:Touch事件分发机制
- Android Touch事件分发机制
- Android,Touch事件分发机制。
- Android Touch事件分发机制
- Android Touch 事件分发机制
- Android:Touch事件分发机制
- Android touch事件分发机制
- Android Touch事件分发机制
- Android Touch事件分发机制
- Android Touch 事件分发机制
- 分析Android的Touch事件分发机制
- An amazing guide to Neural Networks
- VBA_把Excel某一区域的内容读入到数组中/把数组的内容写入到Excel中
- Servlet中PrintWriter和OutputStream两个流不能同时使用
- ViewController生命周期
- Alcatraz的安装和使用,以及Xcode6.2不显示Package manage的问题
- Android 中的Touch事件分发机制
- js serializeArray方法
- 5、Angular中的$timeOut定时器
- word2vec原理概述
- 迭代子模式(Iterator)
- man ascii,cal,xxd,mdfind
- 使用Linux 原始套接字抓取数据链路层上IEC61850-9-2(LE) SV数据包并显示的参考程序
- Objective-C基础学习笔记(七)-类的本质与SEL
- linux上安装配置vsftpd