当前位置: 首页 > news >正文

免费防红短链接生成青岛seo公司网站

免费防红短链接生成,青岛seo公司网站,wordpress的官网,学编程用什么笔记本电脑比较好作者#xff1a;积木zz 这次说下Android中的事件分发机制 从开始点击屏幕开始#xff0c;就会产生从Activity开始到decorview一直到最里层的view一连串事件传递。每一层view或者viewgroup都会首先调用它的dispatchTouchEvent方法#xff0c;然后判断是否就在当前一层消费掉事… 作者积木zz 这次说下Android中的事件分发机制 从开始点击屏幕开始就会产生从Activity开始到decorview一直到最里层的view一连串事件传递。每一层view或者viewgroup都会首先调用它的dispatchTouchEvent方法然后判断是否就在当前一层消费掉事件 view的事件分发 首先上一段伪代码是在书上看到的也是我觉得总结的最好的 public boolean dispatchTouchEvent(MotionEvent event) {boolean isConsume false;if (isViewGroup) {if (onInterceptTouchEvent(event)) {isConsume onTouchEvent(event);} else {isConsume child.dispatchTouchEvent(event);}} else {//isViewisConsume onTouchEvent(event);}return isConsume; }如果当前是viewgroup层级就会判断 onInterceptTouchEvent 是否为true如果为true则代表事件要消费在这一层级不再往下传递。接着便执行当前 viewgroup 的onTouchEvent方法。如果onInterceptTouchEvent为false则代表事件继续传递到下一层级的 dispatchTouchEvent方法接着一样的代码逻辑一直到最里面一层的view。 ok还没完哦到最里面一层就会直接执行onTouchEvent方法这时候view有没有权利拒绝消费事件呢 按道理view作为最底层的应该是没有发言权才对。但是呢秉着公平公正原则view也是可以拒绝的可以在onTouchEvent方法返回false表示他不想消费这个事件。那么这个事件又会怎么处理呢见下面一段伪代码 public void handleTouchEvent(MotionEvent event) {if (!onTouchEvent(event)) {getParent.onTouchEvent(event);} }如果view的onTouchEvent方法返回false那么它的父容器的onTouchEvent又会被调用如果父容器的onTouchEvent又返回false则又交给上一级。一直到最上层也就是Activity的onTouchEvent被调用。 至此消费流程完毕 但是关于onTouchonTouchEvent和onClick又是怎么样的调用关系呢 那就再来一段伪代码 public void consumeEvent(MotionEvent event) {if (setOnTouchListener) {onTouch();if (!onTouch()) {onTouchEvent(event);}} else {onTouchEvent(event);}if (setOnClickListener) {onClick();} }当某一层viewGroup的onInterceptTouchEvent被调用则代表当前层级要消费事件。如果它的onTouchListener被设置了的话则onTouch会被调用如果onTouch的返回值返回true则onTouchEvent不会被调用。如果返回false或者没有设置onTouchListener则会继续调用onTouchEvent。而onClick方法则是设置了onClickListener则会被正常调用。 这里用一张流程图总结下 源码分析 一个触摸事件首先是传到Activity层级然后传到根view通过一层层的viewgroup最终到底最里面一层的view我们来一层层解析 ActivitydispatchTouchEvent 直接上代码 //Activity.javapublic boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() MotionEvent.ACTION_DOWN) {onUserInteraction();}if (getWindow().superDispatchTouchEvent(ev)) {return true;}return onTouchEvent(ev);}public void onUserInteraction() {}这里可以看到onUserInteraction方法是空的主要是调用了getWindow().superDispatchTouchEvent(ev)方法返回true就代表事件消费了。返回false就代表下层没人处理那就直接到了activity的onTouchEvent方法这点跟之前的消费传递也是吻合的。 继续看看superDispatchTouchEvent方法然后就走到了PhoneWindow的superDispatchTouchEvent方法以及DecorView的superDispatchTouchEvent看看代码 //PhoneWindow.javaprivate DecorView mDecor;Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);}//DecorView.javapublic boolean superDispatchTouchEvent(MotionEvent event) {return super.dispatchTouchEvent(event);}这里可以看到依次经过了PhoneWindow到达了DecorViewDecorView是activity的根view也是setcontentView所设置的view的父view它是继承自FrameLayout。所以这里super.dispatchTouchEvent(event)方法其实就是走到了viewgroup的dispatchTouchEvent 方法。 ViewGroupdispatchTouchEvent Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {if (onFilterTouchEventForSecurity(ev)) {// Check for interception,表示是否拦截的字段final boolean intercepted;if (actionMasked MotionEvent.ACTION_DOWN|| mFirstTouchTarget ! null) {//FLAG_DISALLOW_INTERCEPT标志是通过requestDisallowInterceptTouchEvent设置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;}//mFirstTouchTarget赋值while (target ! null) {final TouchTarget next target.next;if (alreadyDispatchedToNewTouchTarget target newTouchTarget) {} else {if (cancelChild) {if (predecessor null) {mFirstTouchTarget next;} else {predecessor.next next;}continue;}}} }这里截取了部分关键的代码首先是两个条件 actionMasked MotionEvent.ACTION_DOWN mFirstTouchTarget ! null 如果满足了其中一个条件才会继续走下去执行onInterceptTouchEvent方法等否则就直接intercepted true表示拦截。 第一个条件很明显就是表示当前事件位按下事件ACTION_DOWN 第二个条件是个字段根据下面的代码可以得知当后面有view消费掉事件的时候这个mFirstTouchTarget字段就会赋值否则就为空。 所以什么意思呢当ACTION_DOWN事件时候一定会执行到后面代码。当其他事件来的时候要看当前viewgroup是否消费了事件如果当前viewgroup已经消费了事件没传到子view那么mFirstTouchTarget字段就为空所以就不会执行到后面的代码就直接消费掉所有事件了。 这就符合了之前的所说的一种机制 某个view一旦开始拦截那么后续事件就全部就给它处理了也不会执行onInterceptTouchEvent方法了 但是两个条件满足了一个就能执行到onInterceptTouchEvent了吗不一定这里看到还有一个判断条件disallowIntercept。这个字段是由requestDisallowInterceptTouchEvent方法设置的后面我们会讲到主要用于滑动冲突意思就是子view告诉你不想让你拦截那么你就不拦截了直接返回false。 ok继续看源码之前的内容我们了解到如果viewgroup不拦截事件应该会传递给子view那在哪里传的呢继续看看dispatchTouchEvent的代码 if (!canceled !intercepted) {final int childrenCount mChildrenCount;if (newTouchTarget null childrenCount ! 0) {final View[] children mChildren;for (int i childrenCount - 1; i 0; i--) {final int childIndex getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child getAndVerifyPreorderedView(preorderedList, children, childIndex);if (childWithAccessibilityFocus ! null) {if (childWithAccessibilityFocus ! child) {continue;}childWithAccessibilityFocus null;i childrenCount - 1;}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();if (preorderedList ! null) {// childIndex points into presorted list, find original indexfor (int j 0; j childrenCount; j) {if (children[childIndex] mChildren[j]) {mLastTouchDownIndex j;break;}}} else {mLastTouchDownIndex childIndex;}mLastTouchDownX ev.getX();mLastTouchDownY ev.getY();newTouchTarget addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget true;break;}// The accessibility focus didnt handle the event, so clear// the flag and do a normal dispatch to all children.ev.setTargetAccessibilityFocus(false);}if (preorderedList ! null) preorderedList.clear();}}}这里可以看到进行了一个子view的遍历其中如果满足两个条件中的一个就跳出。否则就执行dispatchTransformedTouchEvent方法。先看看这两个条件 !child.canReceivePointerEvents()!isTransformedTouchPointInView(x, y, child, null) 看名字是看不出啥了直接看代码吧 protected boolean canReceivePointerEvents() {return (mViewFlags VISIBILITY_MASK) VISIBLE || getAnimation() ! null;}protected boolean isTransformedTouchPointInView(float x, float y, View child,PointF outLocalPoint) {final float[] point getTempPoint();point[0] x;point[1] y;transformPointToViewLocal(point, child);final boolean isInView child.pointInView(point[0], point[1]);if (isInView outLocalPoint ! null) {outLocalPoint.set(point[0], point[1]);}return isInView;}哦原来是这个意思。canReceivePointerEvents方法就代表view是不是可以接受点击事件比如是不是在播放动画。而isTransformedTouchPointInView方法代表点击事件的坐标是不是在这个view的区域上面。 ok如果条件都满足就执行到dispatchTransformedTouchEvent方法了 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;// Canceling motions is a special case. We dont 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);if (child null) {handled super.dispatchTouchEvent(event);} else {handled child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;} }这个方法大家应该都猜到了其实就是执行了child.dispatchTouchEvent(event)。也就是下一层view的dispatchTouchEvent方法呗开始事件的层级传递。 ViewdispatchTouchEvent 到view 层级的时候自然就执行的view的dispatchTouchEvent上代码 public boolean dispatchTouchEvent(MotionEvent event) {boolean result false;if (mInputEventConsistencyVerifier ! null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}final int actionMasked event.getActionMasked();if (actionMasked MotionEvent.ACTION_DOWN) {// Defensive cleanup for new gesturestopNestedScroll();}if (onFilterTouchEventForSecurity(event)) {if ((mViewFlags ENABLED_MASK) ENABLED handleScrollBarDragging(event)) {result true;}//noinspection SimplifiableIfStatementListenerInfo 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 didnt want the rest// of the gesture.if (actionMasked MotionEvent.ACTION_UP ||actionMasked MotionEvent.ACTION_CANCEL ||(actionMasked MotionEvent.ACTION_DOWN !result)) {stopNestedScroll();}return result;}这里可以看到首先会判断li.mOnTouchListener ! null如果不为空就会执行onTouch方法。 根据onTouch方法返回的结果如果为falseresult就为false那么onTouchEvent才会执行。这个逻辑也是符合我们之前说的传递方式。 最后我们再看看view的onTouchEvent都做了什么事 final boolean clickable ((viewFlags CLICKABLE) CLICKABLE|| (viewFlags LONG_CLICKABLE) LONG_CLICKABLE)|| (viewFlags CONTEXT_CLICKABLE) CONTEXT_CLICKABLE;if (clickable || (viewFlags TOOLTIP) TOOLTIP) {switch (action) {case MotionEvent.ACTION_UP:boolean prepressed (mPrivateFlags PFLAG_PREPRESSED) ! 0;if ((mPrivateFlags PFLAG_PRESSED) ! 0 || prepressed) {// take focus if we dont have it already and we should in// touch mode.boolean focusTaken false;if (isFocusable() isFocusableInTouchMode() !isFocused()) {focusTaken requestFocus();}if (!mHasPerformedLongPress !mIgnoreNextUpEvent) {// 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)) {performClickInternal();}}}}mIgnoreNextUpEvent false;break;}return true;}从代码可以得知如果设置了CLICKABLE或者LONG_CLICKABLE那么这个view就会消费事件并且执行performClickInternal方法然后执行到performClick方法。这个performClick方法大家应该都很熟悉就是触发点击的方法其实内部就是执行了onClick方法。 private boolean performClickInternal() {notifyAutofillManagerOnClick();return 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;}return result;}至此源代码也看的差不多了内部其实有很多细节这里也就不一一说明了大家有空可以去研究下。 事件分发的应用requestDisallowInterceptTouchEvent 那既然学会了事件分发机制我们实际工作中会怎么应用呢其实最常见的就是解决滑动冲突的问题。一般有两种解决办法 一种是外部拦截从父view端处理根据情况决定事件是否分发到子view一种是内部拦截从子view端处理根据情况决定是否阻止父view进行拦截其中的关键就是requestDisallowInterceptTouchEvent方法。 第一种方法其实就是在onInterceptTouchEvnet方法里面进行判断返回true还是返回false。 第二种方法就是用到了requestDisallowInterceptTouchEvent方法这个方法的意思就是让父view不要去拦截事件了在dispatchTouchEvent方法里面就有这个标志位FLAG_DISALLOW_INTERCEPT如果disallowIntercept字段为true就不会去执行onInterceptTouchEvent方法而是返回false不拦截事件。 上代码 //外部拦截法父view.java Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {boolean intercepted false;//父view拦截条件boolean parentCanIntercept;switch (ev.getActionMasked()) {case MotionEvent.ACTION_DOWN:intercepted false;break;case MotionEvent.ACTION_MOVE:if (parentCanIntercept) {intercepted true;} else {intercepted false;}break;case MotionEvent.ACTION_UP:intercepted false;break;}return intercepted;}外部拦截很简单就是判断条件然后决定是否进行拦截。 //父view.java Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {if (ev.getActionMasked() MotionEvent.ACTION_DOWN) {return false;} else {return true;}}//子view.javaOverridepublic boolean dispatchTouchEvent(MotionEvent event) {//父view拦截条件boolean parentCanIntercept;switch (event.getActionMasked()) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE:if (parentCanIntercept) {getParent().requestDisallowInterceptTouchEvent(false);}break;case MotionEvent.ACTION_UP:break;}return super.dispatchTouchEvent(event);}感觉内部拦截有点复杂呀还要重写父view的方法这里分析下为什么要去这么写 父view ACTION_DOWN的时候不能拦截因为如果拦截那么后续事件也就跟子view无关了父view 其他事件的时候要返回true表示拦截。因为onInterceptTouchEvent方法的调用是被FLAG_DISALLOW_INTERCEPT标志位所控制所以子view需要父view拦截的时候才会走到这个onInterceptTouchEvent方法中来那么这时候要保证方法中一定是要拦截的。 Android 学习笔录 Android 性能优化篇https://qr18.cn/FVlo89 Android 车载篇https://qr18.cn/F05ZCM Android 逆向安全学习笔记https://qr18.cn/CQ5TcL Android Framework底层原理篇https://qr18.cn/AQpN4J Android 音视频篇https://qr18.cn/Ei3VPD Jetpack全家桶篇内含Composehttps://qr18.cn/A0gajp Kotlin 篇https://qr18.cn/CdjtAF Gradle 篇https://qr18.cn/DzrmMB OkHttp 源码解析笔记https://qr18.cn/Cw0pBD Flutter 篇https://qr18.cn/DIvKma Android 八大知识体https://qr18.cn/CyxarU Android 核心笔记https://qr21.cn/CaZQLo Android 往年面试题锦https://qr18.cn/CKV8OZ 2023年最新Android 面试题集https://qr18.cn/CgxrRy Android 车载开发岗位面试习题https://qr18.cn/FTlyCJ 音视频面试题锦https://qr18.cn/AcV6Ap
http://www.pierceye.com/news/171570/

相关文章:

  • 二手站网站怎做福州网站建设的公司哪家好
  • dw如何做网站后台佛山行业网站建设
  • 如何做网站轮播大图简单网页制作成品代码
  • 网站怎么做uc整合查企业网站
  • 网站没被收录什么原因网站排名点击工具
  • 江西南昌建设厅网站商品展示软件
  • 眼镜企业网站建设方案2015做那些网站致富
  • 创建个人网站的流程建设网站聊天室
  • cms 学校网站上海模板网站
  • 网站建设投资风险分析公司做的网站费用如何做账
  • 网站建设费用核算科目DW做的网页用网站打不开
  • wordpress标签搜索引擎嘉兴市做网站优化
  • 网站更换关键词怎么做好wordpress post fonts
  • 厦门优化网站排名网站备案转服务器
  • 怎样做pdf电子书下载网站做旅行攻略的网站
  • 怎样做网站推广啊抖音网站的flash怎么做
  • 网站建设小说网站建设目标是什么意思
  • 如何做一个好的网站中英文网站好处
  • wordpress站点版权设置晋中建设集团网站
  • 怎么夸一个网站做的好看烟台百度网站推广
  • 佛山市网站建设分站多少钱企业门户账号是什么
  • 大中型网站开发价格铜山区建设局局网站周保春
  • 为什么有人做商城优惠券网站卖科技风格设计网站
  • 企业网站的需求分析是做网站编辑还是做平面设计
  • 超酷 flash 网站淮南网红餐厅
  • 湛江网站建设开发株洲关键词seo优化服务商
  • 女的有没有做网站的十大经典随身空间小说推荐
  • 江西做网站哪家好监理证查询网
  • 北京驾校网站建设网络哪里能接活做网站
  • 建设网站公司排名西宁网站建设优化案例