android 事件分发
2015-10-11 20:51
465 查看
1.背景
在写android程序时,由于设计需要,经常要自定义控件,那么假设:有一个控件,内部可以填充子控件,满足控件的滑动且子控件可点击;是不是很常见?这个假设的实现:nice的遇见频道。你想象的,它应该是本来就可以滑动,然后子控件又可以点击,但事实不是你理解的那样。我们面临两个问题 :1.这个事件到底是如何分发和相应的2.自定义控件时,如何处理,可以完成,控件滑动,子控件可点击。
2.准备知识
Android 是怎么捕获屏幕事件的?追到源码里面window这个类,负责屏幕上事件的处理,window是怎么处理的,framework的这部分代码不开放。源码(Android 2.3)中是我们关键看这几个方法,然后再总结:
a.Activity
1.dispatchTouchEvent(MotionEvent ev);
2.onTouchEvent(MotionEvent event);
b.ViewGroup
1.dispatchTouchEvent(MotionEvent ev);//viewGroup分发事件,有些长
2.onTouchEvent(MotionEvent event);//真正处理事件的
3.onInterceptTouchEvent(MotionEvent ev);//截断事件传递
c.View
1.dispatchTouchEvent(MotionEvent event);
2.onTouchEvent(MotionEvent event);
3查看源码结论:
4自定义控件中,如何处理,滑动和子控件的点击?下篇
在写android程序时,由于设计需要,经常要自定义控件,那么假设:有一个控件,内部可以填充子控件,满足控件的滑动且子控件可点击;是不是很常见?这个假设的实现:nice的遇见频道。你想象的,它应该是本来就可以滑动,然后子控件又可以点击,但事实不是你理解的那样。我们面临两个问题 :1.这个事件到底是如何分发和相应的2.自定义控件时,如何处理,可以完成,控件滑动,子控件可点击。
2.准备知识
Android 是怎么捕获屏幕事件的?追到源码里面window这个类,负责屏幕上事件的处理,window是怎么处理的,framework的这部分代码不开放。源码(Android 2.3)中是我们关键看这几个方法,然后再总结:
a.Activity
1.dispatchTouchEvent(MotionEvent ev);
2.onTouchEvent(MotionEvent event);
public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { onUserInteraction();//这个是处理活动状态,关,帮助活动管理状态栏通知; } if (getWindow().superDispatchTouchEvent(ev){ //window实质上来完成事件的分发 return true; } return onTouchEvent(ev); } public boolean onTouchEvent(MotionEvent event) { // 默认是不消费事件的 return false; }
b.ViewGroup
1.dispatchTouchEvent(MotionEvent ev);//viewGroup分发事件,有些长
2.onTouchEvent(MotionEvent event);//真正处理事件的
3.onInterceptTouchEvent(MotionEvent ev);//截断事件传递
/** * {@inheritDoc} */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { //本次事件是否被过滤,过滤不向下传递 if (!onFilterTouchEventForSecurity(ev)) { return false; } final int action = ev.getAction(); final float xf = ev.getX(); final float yf = ev.getY(); final float scrolledXFloat = xf + mScrollX; final float scrolledYFloat = yf + mScrollY; final Rect frame = mTempRect; //是否允许截断 boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (action == MotionEvent.ACTION_DOWN) { if (mMotionTarget != null) { // this is weird, we got a pen down, but we thought it was // already down! // XXX: We should probably send an ACTION_UP to the current // target. mMotionTarget = null; } // If we're disallowing intercept or if we're allowing and we didn't // intercep 两个参数控制 if (disallowIntercept || !onInterceptTouchEvent(ev)) { // reset this event's action (just to protect ourselves) ev.setAction(MotionEvent.ACTION_DOWN); // We know we want to dispatch the event down, find a child // who can handle it, start with the front-most child. final int scrolledXInt = (int) scrolledXFloat; final int scrolledYInt = (int) scrolledYFloat; final View[] children = mChildren; final int count = mChildrenCount; // 遍历,检查那个子控件落在点击位置上 for (int i = count - 1; i >= 0; i--) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { child.getHitRect(frame); if (frame.contains(scrolledXInt, scrolledYInt)) { // offset the event to the view's coordinate system final float xc = scrolledXFloat - child.mLeft; final float yc = scrolledYFloat - child.mTop; ev.setLocation(xc, yc); child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; if (child.dispatchTouchEvent(ev)) { // Event handled, we have a target now. mMotionTarget = child; return true; } // The event didn't get handled, try the next view. // Don't reset the event's location, it's not // necessary here. } } } } } boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) || (action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) { // Note, we've already copied the previous state to our local // variable, so this takes effect on the next event mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT; } // The event wasn't an ACTION_DOWN, dispatch it to our target if // we have one 那个子控件 final View target = mMotionTarget; if (target == null) { // We don't have a target, this means we're handling the // event as a regular view. ev.setLocation(xf, yf); if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { ev.setAction(MotionEvent.ACTION_CANCEL); mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; } return super.dispatchTouchEvent(ev); } // if have a target, see if we're allowed to and want to intercept its // events // 再次判断是否有拦截 if (!disallowIntercept && onInterceptTouchEvent(ev)) { final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); if (!target.dispatchTouchEvent(ev)) { // target didn't handle ACTION_CANCEL. not much we can do // but they should have. } // clear the target mMotionTarget = null; // Don't dispatch this event to our own view, because we already // saw it when intercepting; we just want to give the following // event to the normal onTouchEvent(). return true; } if (isUpOrCancel) { mMotionTarget = null; } // finally offset the event to the target's coordinate system and // dispatch the event. final float xc = scrolledXFloat - (float) target.mLeft; final float yc = scrolledYFloat - (float) target.mTop; ev.setLocation(xc, yc); if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { ev.setAction(MotionEvent.ACTION_CANCEL); target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; mMotionTarget = null; } //交给子控件的分发处理 //注意整个过程没有看到调用ViewGroup自己的ontouchEvent方法 return target.dispatchTouchEvent(ev); } //默认是不拦截事件分发的 public boolean onInterceptTouchEvent(MotionEvent ev) { return false; }
c.View
1.dispatchTouchEvent(MotionEvent event);
2.onTouchEvent(MotionEvent event);
public boolean dispatchTouchEvent(MotionEvent event) { if (!onFilterTouchEventForSecurity(event)) { return false; } if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && mOnTouchListener.onTouch(this, event)) { return true; } return onTouchEvent(event); }
3查看源码结论:
4自定义控件中,如何处理,滑动和子控件的点击?下篇
相关文章推荐
- 如何解决ArcGIS Runtime SDK for Android中文标注无法显示的问题
- Android 6.0新特性
- Android Socket 编程
- Android音频系统之AudioTrack(二)
- Android 传感器 (详解一)—— 简单介绍传感器的使用步骤
- Android音频系统之AudioTrack(一)
- Android 自定义系统对话框
- Android拍照后Bitmap内存溢出的解决办法
- Android~外部类监听器~实现Activity的跳转
- Android应用的四大基本组件介绍
- android学习之1:拍照保存图片
- Android 使用AsyncTaskLoader获取手机联系人
- Android 使用CursorLoader获取手机短信
- android上传图片(及普通参数)到服务器(j2ee后台服务器,ssh框架)
- Android 获取手机的联系人进行操作
- Android 操作软键盘
- Android 容器焦点设置 android:descendantFocusability
- Android数据存储方式之——Shareprefences
- Android按钮跳转页面
- Android Design ItemTouchHelper实现酷炫列表的移行和滑动删除效果