View事件解析(下)
来源:互联网 发布:动态壁纸软件情侣 编辑:程序博客网 时间:2024/05/20 09:07
上一篇文章主要分析了一下事件的分发流程(view事件分析(上)这里写链接内容),这篇文章主要是通过源码来分析一下onTouchEvent,dispatchTouchEvent源码内部。
还是沿用上一篇文章的demo,采用sdk19的版本查看源码。 从上一篇文章中我们知道分发首先是从父类开始的,如果父类不拦截,然后交给子类自己分发,那我们先从父类的dispatchTouchEvent讲起。
final int childrenCount = mChildrenCount; if (newTouchTarget == null && childrenCount != 0) { final float x = ev.getX(actionIndex); final float y = ev.getY(actionIndex); // Find a child that can receive the event. // Scan children from front to back. final View[] children = mChildren; 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; } } }
这里挑选了主要代码,第10行开始for循环这里循环了所有的子类,然后寻找目标child(之前有一篇文章对这块做过详细的介绍dispatchTouchEvent小分析这里写链接内容)。这里是在第14行的if语句中进行判断的,如果能接收事件或者在子child的范围内,这里同时取反,如果不满足则直接跳过进行下一个循环,下面我们重点看一下是怎么判断是否在child范围内的。
protected boolean isTransformedTouchPointInView(float x, float y, View child, PointF outLocalPoint) { float localX = x + mScrollX - child.mLeft; float localY = y + mScrollY - child.mTop; if (! child.hasIdentityMatrix() && mAttachInfo != null) { final float[] localXY = mAttachInfo.mTmpTransformLocation; localXY[0] = localX; localXY[1] = localY; child.getInverseMatrix().mapPoints(localXY); localX = localXY[0]; localY = localXY[1]; } //这里调用了下面的pointInView的方法来判断是都在其范围内 final boolean isInView = child.pointInView(localX, localY); if (isInView && outLocalPoint != null) { outLocalPoint.set(localX, localY); } return isInView; } /** * Determines whether the given point, in local coordinates is inside the view. */ /*package*/ final boolean pointInView(float localX, float localY) { return localX >= 0 && localX < (mRight - mLeft) && localY >= 0 && localY < (mBottom - mTop);
ok,这里已经分析了怎么确定了目标child的,确定了child后将自己分发给child,在上面dispatchTouchEvent中的第28行调用了这句话dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign),进入到源码看看,这里仍然展示了部分重要代码:
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel, View child, int desiredPointerIdBits) { final boolean handled; // Canceling motions is a special case. We don't need to perform any transformations // or filtering. The important part is the action, not the contents. final int oldAction = event.getAction(); if (cancel || oldAction == MotionEvent.ACTION_CANCEL) { event.setAction(MotionEvent.ACTION_CANCEL); //上面已经确定了child,所有直接走了else if (child == null) { handled = super.dispatchTouchEvent(event); } else { //这里handle的值直接取决于child的dispatchTouchEvent返回值 handled = child.dispatchTouchEvent(event); } event.setAction(oldAction); return handled; } ........ }
如果在这里子类的dispatchTouchEvent返回了false,也就是在viewGroup中if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) 这句,进入不到了if语句,下面的newTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;也无法进行赋值,也就是需要重新进行找target。这时候在进行for循环找是否有符合的对应child。下面我们来打log瞅瞅,将之前的代码小改了一下,显示了Btn3。
<com.example.ontouchtext.MyFrameLayout android:id="@+id/framelayout" android:layout_width="wrap_content" android:layout_height="wrap_content"> <com.example.ontouchtext.Btn2 android:id="@+id/btn2" android:layout_width="100dp" android:layout_height="100dp" android:background="#aaaaaa" android:text="btn2" /> <com.example.ontouchtext.Btn3 android:id="@+id/btn3" android:layout_width="100dp" android:layout_height="100dp" android:layout_marginLeft="50dp" android:layout_marginTop="50dp" android:background="#1babaa" android:text="btn3" android:visibility="visible" /> </com.example.ontouchtext.MyFrameLayout>
这里点击了btn2和btn3重合的区域,主要是为了btn3也能满足pointInView。然后讲btn2的dispatchTouchEvent返回了false。看看log:
正和我们预期的一样,先走了父类的分发,然后进入到for循环,找到btn2,btn2的dispatchTouchEvent返回了false,并没有给目标child赋值,这时候for循环接着找,然后找到了btn3。所以看到了我们上面的log。
下面我把btn的dispatchTouchEvent的返回值改为true,然后再看看log打印值:
看到的结果也是我们预期的,并没有走btn3,也就是btn2已经成为了目标child。
接着我们来说说onTouchEvent方法,我们常常说onTouchEvent方法返回true是用来消费掉事件的。我们来看看源码研究一下到底这个有啥功效。同样我们来看看dispatchTouchEvent方法,不过这次是看view的方法非viewGroup。
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; }
这里是不是有很惊喜的发现,第10行看起,如果onTouch返回了false,那么这里走到了第14行的if判断,这里如果onTouchEvent返回true,那么整个dispatchTouchEvent将返回true,否则第22行直接返回了false,也就是上面刚讲到的,view的dispatchTouchEvent返回false,不能成为目标child。所以该事件分发到btn2时没有进行消费。
这里也可以看到如果onTouch返回了true,也会整个view的dispatchTouchEvent返回true,同样也能消费掉这个事件。
最后如果btn2,btn3,和父类ViewGroup都不消费这个事件,到底事件会怎么处理了。下面接着改了一下demo。
将btn2,btn2,MyFragmentLayout的onTouchEvent全部返回false。
同时在MainActivity加了onTouchEvent方法:
public class MainActivity extends Activity { private MyFrameLayout frameLayout; private Button btn1; private Btn2 btn2; private Btn3 btn3; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); frameLayout = (MyFrameLayout) findViewById(R.id.framelayout); btn1 = (Button) findViewById(R.id.btn1); btn2 = (Btn2) findViewById(R.id.btn2); btn2.bringToFront(); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: System.out.println("Activity---onTouchEvent---DOWN"); break; case MotionEvent.ACTION_MOVE: System.out.println("Activity---onTouchEvent---MOVE"); break; case MotionEvent.ACTION_UP: System.out.println("Activity---onTouchEvent---UP"); break; default: break; } return super.onTouchEvent(event); }}
同样点击一下btn2和btn3重合处,打印log
哈哈,最后都不处理,会把事件交给activity进行处理。先交给btn2,btn2的onTouchEvent返回false说我不消费,然后接着到了btn3,btn3也返回false说我也不消费,返回到了MyFrameLayout,结果他也返回了fasle,最后就交给了activity处理了。
嗯,到此结束了,以上是我对view事件理解。(view事件分析(上)这里写链接内容)
- View事件解析(下)
- view事件解析(上)
- Android自定义View(二)---拉刷新ListView 下之事件分发源码解析
- Android View 事件分发机制 源码解析(View篇)
- View的事件源码解析
- View的事件体系(下)(事件分发,滑动冲突)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- Android View 事件分发机制 源码解析 (上)
- 队列的模拟
- android自定义dialog实现app换肤功能
- iOS 使用collectionView实现无线轮播
- svn operation on linux
- 王家林老师广收大数据门徒,有志同道合或想了解大数据的朋友,请加入我们团队吧
- View事件解析(下)
- 图片文字提取之路-01预处理
- java泛型程序设计——泛型类的静态上下文中类型变量无效+不能抛出或捕获泛型类的实例
- LA 4794(Sharing Chocolate-记忆化搜索)
- 有趣的linux小命令一一oneko猫捉老鼠
- Android Studio 快捷键中英文对译
- SQL数据库对某一字段下的值进行求和,然后倒序排序
- 欢迎使用CSDN-markdown编辑器
- 位域初识