08.Android之View事件问题

栏目: Android · 发布时间: 6年前

目录介绍

  • 8.0.0.1 简述Android的事件分发机制?dispatchTouchEvent方法的作用是什么?说下View和ViewGroup分发事件?
  • 8.0.0.2 onInterceptTouchEvent方法作用是什么?onTouchEvent的方法的作用是什么?
  • 8.0.0.4 滑动冲突有哪些场景?滑动冲突处理原则是什么?滑动冲突解决办法有哪些?分别是如何解决的?
  • 8.0.0.5 onTouch()、onTouchEvent()和onClick()关系是怎样的,哪一个先执行?如果设置了onClickListener, 但是onClick()没有调用,可能产生的原因?
  • 8.0.0.6 View滑动有哪些方法?这些方法分别是如何实现滑动的?分别有什么优缺点?
  • 8.0.0.7 事件的传递规则是什么?View处理事件的优先级?点击事件传递过程遵循如下顺序?事件传递规则要点?
  • 8.0.0.8 Scroller的作用?Scroller的要点有哪些?Scroller的使用步骤?Scroller工作原理?
  • 8.0.0.9 Activity事件分发的过程?Window事件分发?DecorView的事件分发?根View的事件分发?
  • 8.0.1.0 GestureDetector是干什么用的?GestureDetector作用和注意点?有哪些常用的监听方法?
  • 8.0.1.2 View的滑动方式?如何让控件滚动到某一位置?scrollTo()和scrollBy()的区别?Scroller是什么?
  • 8.0.1.4 谈一谈View的工作原理,执行流程,MeasureSpec是什么?有什么作用?
  • 8.0.1.6 SurfaceView和View的区别,说一下SurfaceView的工作原理,为何不会导致页面卡顿?

好消息

  • 博客笔记大汇总【15年10月到至今】,包括 Java 基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 链接地址: https://github.com/yangchong2...
  • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!

8.0.0.1 简述Android的事件分发机制?dispatchTouchEvent方法的作用是什么?说下View和ViewGroup分发事件?

  • 简述Android的事件分发机制? 技术博客大总结

    • 事件分发顺序:Activty->ViewGroup->View
    • 主要方法:dispatchTouchEvent-分发事件、onInterceptTouchEvent-当前View是否拦截该事件、onTouchEvent-处理事件
    • 1.父View调用dispatchTouchEvent开启事件分发。
    • 2.父View调用onInterceptTouchEvent判断是否拦截该事件,一旦拦截后该事件的后续事件(如DOWN之后的MOVE和UP)都直接拦截,不会再进行判断。
    • 3.如果父View进行拦截,父View调用onTouchEvent进行处理。
    • 4.如果父View不进行拦截,会调用子View的dispatchTouchEvent进行事件的层层分发。
  • dispatchTouchEvent方法的作用是什么?

    • 用于进行事件的分发
    • 只要事件传给当前View,该方法一定会被调用
    • 返回结果受到当前View的onTouchEvent和下级View的dispatchTouchEvent影响
    • 表示是否消耗当前事件
  • ViewGroup事件分发伪代码

    • 08.Android之View事件问题
  • View事件分发伪代码

    • 08.Android之View事件问题
  • View和ViewGroup在dispatchTouchEvent上的区别

    • ViewGroup在dispatchTouchEvent()中会进行事件的分发。
    • View在dispatchTouchEvent()中会对该事件进行处理。 技术博客大总结

8.0.0.2 onInterceptTouchEvent方法作用是什么?onTouchEvent的方法的作用是什么?

  • onInterceptTouchEvent方法作用是什么?

    • 在dispatchTouchEvent的内部调用,用于判断是否拦截某个事件
    • View和ViewGroup在onInterceptTouchEvent上的区别:

      • View没有该方法,View会处理所有收到的事件,但不一定会消耗该事件。
      • onInterceptTouchEvent是ViewGroup中添加的方法,用于判断是否拦截该事件。
  • onTouchEvent的方法的作用是什么? 技术博客大总结

    • 在dispatchTouchEvent的中调用,用于处理点击事件
    • 返回结果表示是否消耗当前事件

8.0.0.4 滑动冲突有哪些场景?滑动冲突处理原则是什么?滑动冲突解决办法有哪些?分别是如何解决的?

  • 滑动冲突有哪些场景?

    • 内层和外层滑动方向不一致:一个垂直,一个水平。比如轮播图ViewPager和ScrollView
    • 内存和外层滑动方向一致:均垂直or水平。比如scrollView和RecyclerView
    • 前两者层层嵌套。比如ScrollView和RecyclerView(recyclerView中又嵌套recyclerView)
  • 滑动冲突处理原则

    • 对于内外层滑动方向不同,只需要根据滑动方向来给相应控件拦截
    • 对于内外层滑动方向相同,需要根据业务来进行事件拦截,规定何时让外部View拦截事件何时由内部View拦截事件。
    • 前两者嵌套的情况,根据前两种原则层层处理即可。
  • 滑动冲突解决办法有哪些? 技术博客大总结

    • 外部拦截法:指点击事件都先经过父容器的拦截处理,如果父容器需要此事件就拦截,否则就不拦截。具体方法:需要重写父容器的onInterceptTouchEvent方法,在内部做出相应的拦截。
    • 内部拦截法:指父容器不拦截任何事件,而将所有的事件都传递给子容器,如果子容器需要此事件就直接消耗,否则就交由父容器进行处理。具体方法:需要配合requestDisallowInterceptTouchEvent方法。
  • 外部拦截解决滑动冲突法

    • 外部拦截法要点

      • 父容器的onInterceptTouchEvent方法中处理
      • ACTION_DOWN不拦截,一旦拦截会导致后续事件都直接交给父容器处理。
      • ACTION_MOVE中根据情况进行拦截,拦截:return true,不拦截:return false(外部拦截核心)
      • ACTION_UP不拦截,如果父控件拦截UP,会导致子元素接收不到UP进一步会让onClick方法无法触发。此外UP拦截也没什么用。
    • onClick方法生效的两个条件?

      • View可以点击
      • 接收到了DOWN和UP事件
    • 外部拦截,自定义ScrollView,这块可以看我的博客:

8.0.0.5 onTouch()、onTouchEvent()和onClick()关系是怎样的,哪一个先执行?如果设置了onClickListener, 但是onClick()没有调用,可能产生的原因?

  • onTouch()、onTouchEvent()和onClick()关系是怎样的,哪一个先执行?

    • onTouch->onTouchEvent->onClick

      • 当一个View需要处理事件时,如果它设置了OnTouchListener,那么OnTouchListener的onTouch方法会被回调。
      • 这时事件如何处理还得看onTouch的返回值,如果返回false,则当前View的onTouchEvent方法会被调用;如果返回true,那么onTouchEvent方法将不会被调用。由此可见,给View设置的onTouchListener,其优先级比onTouchEvent要高。
      • 如果当前方法中设置了onClickListener,那么它的onClick方法会被调用。可以看出,常用的OnClickListener,其优先级别最低。
  • 如果设置了onClickListener, 但是onClick()没有调用,可能产生的原因? 技术博客大总结

    • 父View拦截了事件,没有传递到当前View
    • View的Enabled = false(setEnabled(false)): view处于不可用状态,会直接返回。
    • View的Clickable = false(setClickablesetLongClickable(false)):view不可以点击,不会执行onClick
    • View设置了onTouchListener,且消耗了事件。会提前返回。
    • View设置了TouchDelegate,且消耗了事件。会提前返回。

8.0.0.6 View滑动有哪些方法?这些方法分别是如何实现滑动的?分别有什么优缺点?

  • View滑动有哪些方法?

    • layout:对View进行重新布局定位。在onTouchEvent()方法中获得控件滑动前后的偏移。然后通过layout方法重新设置。
    • offsetLeftAndRight和offsetTopAndBottom:系统提供上下/左右同时偏移的API。onTouchEvent()中调用
    • LayoutParams: 更改自身布局参数
    • scrollTo/scrollBy: 本质是移动View的内容,需要通过父容器的该方法来滑动当前View
    • Scroller: 平滑滑动,通过重载computeScroll(),使用scrollTo/scrollBy完成滑动效果。
    • 属性动画: 动画对View进行滑动 技术博客大总结
    • ViewDragHelper: 谷歌提供的辅助类,用于完成各种拖拽效果。
  • Layout实现滑动

    • 08.Android之View事件问题
  • offsetLeftAndRight和offsetTopAndBottom实现滑动

    • 08.Android之View事件问题
  • LayoutParams实现滑动

    • 通过父控件设置View在父控件的位置,但需要指定父布局的类型,不好
    • 用ViewGroup的MariginLayoutParams的方法去设置margin

      //方法一:通过布局设置在父控件的位置。但是必须要有父控件, 而且要指定父布局的类型,不好的方法。 
      RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams(); 
      layoutParams.leftMargin = getLeft() + offsetX; 
      layoutParams.topMargin = getTop() + offsetY; 
      setLayoutParams(layoutParams);
      /**===============================================
       * 方法二:用ViewGroup的MarginLayoutParams的方法去设置marign
       * 优点:相比于上面方法, 就不需要知道父布局的类型。
*===============================================*/ 
    ViewGroup.MarginLayoutParams mlayoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
    mlayoutParams.leftMargin = getLeft() + offsetX; 
    mlayoutParams.topMargin = getTop() + offsetY; 
    setLayoutParams(mlayoutParams);
    ```
  • scrollToscrollBy实现滑动

    • 都是View提供的方法。
    • scrollTo-直接到新的x,y坐标处。
    • scrollBy-基于当前位置的相对滑动。
    • scrollBy-内部是调用scrollTo.
    • scrollToscrollBy, 效果是移动View的内容,因此需要在View的父控件中调用。
    • scrollTo/By内部的mScrollX和mScrollY的意义

      • mScrollX的值,相当于手机屏幕相对于View左边缘向右移动的距离,手机屏幕向右移动时,mScrollX的值为正;手机屏幕向左移动(等价于View向右移动),mScrollX的值为负。
      • mScrollY和X的情况相似,手机屏幕向下移动,mScrollY为+正值;手机屏幕向上移动,mScrollY为-负值。
      • mScrollX/Y是根据第一次滑动前的位置来获得的,例如:第一次向左滑动200(等于手机屏幕向右滑动200),mScrollX = 200;第二次向右滑动50, mScrollX = 200 + (-50)= 150,而不是(-50)。
  • 动画实现滑动的方法

    • 可以通过传统动画或者属性动画的方式实现
    • 传统动画需要通过设置fillAfter为true来保留动画后的状态(但是无法在动画后的位置进行点击操作,这方面还是属性动画好)
    • 属性动画会保留动画后的状态,能够点击。 技术博客大总结
  • ViewDragHelper

    • 通过ViewDragHelper去自定义ViewGroup让其子View具有滑动效果。不过用的很少

8.0.0.7 事件的传递规则是什么?View处理事件的优先级?点击事件传递过程遵循如下顺序?事件传递规则要点?

  • 事件的传递规则是什么?

    • 点击事件产生后,会先传递给根ViewGroup,并调用dispatchTouchEvent
    • 之后会通过onInterceptTouchEvent判断是否拦截该事件,如果true,则表示拦截并交给该ViewGroup的onTouchEvent方法进行处理
    • 如果不拦截,则当前事件会传递给子元素,调用子元素的dispatchTouchEvent,如此反复直到事件被处理
  • View处理事件的优先级?

    • 在View需要处理事件时,会先调用OnTouchListener的onTouch方法,并判断onTouch的返回值
    • 返回true,表示处理完成,不会调用onTouchEvent方法
    • 返回false,表示未完成,调用onTouchEvent方法进行处理
    • 可见,onTouchEvent的优先级没有OnTouchListener高
    • onTouchEvent没有消耗的话就会交给TouchDelegate的onTouchEvent去处理。
    • 如果最后事件都没有消耗,会在onTouchEvent中执行performClick()方法,内部会执行OnClickListener的onClick方法,优先级最低,属于事件传递尾端
  • 点击事件传递过程遵循如下顺序? 技术博客大总结

    • Activity->Window->View->分发
    • 如果View的onTouchEvent返回false,则父容器的onTouchEvent会被调用,最终可以传递到Activity的onTouchEvent
  • 事件传递规则要点?

    • View一旦拦截事件,则整个事件序列都由它处理(ACTION_DOWNUP等),onInterceptTouchEvent不会再调用(因为默认都拦截了)
    • 但是一个事件序列也可以通过特殊方法交给其他View处理(onTouchEvent)
    • 如果View开始处理事件(已经拦截),如果不消耗ACTIO_DOWN事件(onTouchEvent返回false),则同一事件序列的剩余内容都直接交给父onTouchEvent处理
    • View消耗了ACTION_DOWN,但不处理其他的事件,整个事件序列会消失(父onTouchEvent)不会调用。这些消失的点击事件最终会传给Activity处理。
    • ViewGroup默认不拦截任何事件(onInterceptTouchEvent默认返回false)
    • View没有onInterceptTouchEvent方法,一旦有事件传递给View,onTouchEvent就会被调用
    • View的onTouchEvent默认都会消耗事件return true, 除非该View不可点击(clickable和longClickable同时为false)
    • View的enable属性不影响onTouchEvent的默认返回值。即使是disable状态。
    • onClick的发生前提是当前View可点击,并且收到了down和up事件
    • 事件传递过程是由父到子,层层分发,可以通过requestDisallowInterceptTouchEvent让子元素干预父元素的事件分发(ACTION_DOWN除外)

8.0.0.8 Scroller的作用?Scroller的要点有哪些?Scroller的使用步骤?Scroller工作原理?

  • Scroller的作用?

    • 用于封装滑动
    • 提供了基于时间的滑动偏移值,但是实际滑动需要我们去负责。
  • Scroller的要点有哪些?

    • 调用startScroll方法时,Scroller只是单纯的保存参数
    • 之后的invalidate方法导致的View重绘
    • View重绘之后draw方法会调用自己实现的computeScroll(),才真正实现了滑动
  • Scroller的使用步骤?

    • 08.Android之View事件问题
  • Scroller工作原理? 技术博客大总结

    • Scroller本身不能实现View的滑动,需要配合View的computeScroll方法实现弹性滑动
    • 不断让View重绘,每一次重绘距离滑动的开始时间有一个时间间隔,通过该时间可以得到View当前的滑动距离
    • View的每次重绘都会导致View的小幅滑动,多次小幅滑动就组成了弹性滑动

8.0.0.9 Activity事件分发的过程?Window事件分发?DecorView的事件分发?根View的事件分发?

  • Activity事件分发的过程?

    • 事件分发过程:Activity->Window->Decor View(当前界面的底层容器,setContentView的View的父容器)->ViewGroup->View
    • Activity的dispatchTouchEvent,会交给Window处理(getWindow().superDispatchTouchEvent()),
    • 返回true:事件全部结束
    • 返回false:所有View都没有处理(onTouchEvent返回false),则调用Activity的onTouchEvent
  • Window事件分发?

    • Window和superDispatchTouchEvent分别是抽象类和抽象方法
    • Window的实现类是PhoneWindow
    • PhoneWindow的superDispatchTouchEvent()直接调用mDecor.superDispatchTouchEvent(),也就是直接传给了DecorView
  • DecorView的事件分发?

    • DecorView继承自FrameLayout
    • DecorView的superDispatchTouchEvent()会调用super.dispatchTouchEvent()——也就是ViewGroup的dispatchTouchEvent方法,之后就会层层分发下去。
  • 根View的事件分发? 技术博客大总结

    • 顶层View调用dispatchTouchEvent
    • 调用onInterceptTouchEvent方法
    • 返回true,事件由当前View处理。如果有onTouchiListener,会执行onTouch,并且屏蔽掉onTouchEvent。没有则执行onTouchEvent。如果设置了onClickListener,会在onTouchEvent后执行onClickListener
    • 返回false,不拦截,交给子View重复如上步骤。

8.0.1.0 GestureDetector是干什么用的?GestureDetector作用和注意点?有哪些常用的监听方法?

  • GestureDetector作用和注意点?

    • 探测手势和事件,需要通过提供的MotionEvent
    • 该类仅能用于touch触摸提供的MotionEvent,不能用于traceball events(追踪球事件)
    • 可以在自定义View中重写onTouchEvent()方法并在里面用GestureDetector接管。
    • 可以在View的setOnTouchListener的onTouch中将点击事件交给GestureDetector接管。
  • 有哪些常用的监听方法?

    • OnGestureListener
    • OnDoubleTapListener
    • OnContextClickListener
    • SimpleOnGestureListener
  • OnGestureListener

    • OnGestureListener作用 技术博客大总结

      • 用于在手势产生时,去通知监听者。
      • 该监听器会监听所有的手势,如果只需要监听一部分可以使用SimpleOnGestureListener
    • OnGestureListener能监听哪些手势

      • 按下操作。
      • 按下之后,Move和Up之前。用于提供视觉反馈告诉用户已经捕获了他们的行为。
      • 抬起操作。
      • 滑动操作(由Down MotionEvent e1触发,当前是Move MotionEvent e2)
      • 长按操作。
      • 猛扔操作。
      • 所有有返回值的回调方法,return true-消耗该事件;return false-不消耗该事件
  • OnDoubleTapListener

    • OnDoubleTapListener作用

      • 监听“双击操作”
      • 监听“确认的单击操作”—该单击操作之后的操作无法构成一次双击。
    • OnDoubleTapListener能监听哪些手势?

      • 单击操作。
      • 双击操作.
      • 双击操作之间发生了down、move或者up事件。

8.0.1.2 View的滑动方式?如何让控件滚动到某一位置?scrollTo()和scrollBy()的区别?Scroller是什么?

  • View的滑动方式?

    • 三种方式:

      • a. 通过View本身提供的scrollTo/scrollBy方法

        • 移动的是View的内容,View本身不移动
      • b. 通过动画给View施加平移效果实现滑动

        • 通过补间动画移动的View的影像,View本身位置不发生改变。通过属性动画移动view的影像,view本身位置会发生改变。
      • c. 通过改变View的LayoutParams使View重新布局实现滑动

        • 改变布局参数,代码如下:
        MarginLayoutParams params = (MarginLayoutParams) mButton.getLayoutParams();
        params.width += 10;
        params.height += 10;
        mButton.setLayoutParams(params);
    • 三种方法的使用对比

      • scrollTo/scrollBy:操作简单,适合对View内容的滑动;
      • 动画:操作简单,主要适合于没有交互的View和实现复杂的动画效果;
      • 改变布局参数:操作稍微复杂,适用于有交互的View。
  • scrollTo()和scrollBy() 技术博客大总结

    • scrollBy内部调用了scrollTo,它是基于当前位置的相对滑动;而scrollTo是绝对滑动,因此如果利用相同输入参数多次调用scrollTo()方法,由于View初始位置是不变只会出现一次View滚动的效果而不是多次。
    • 引申:两者都只能对view内容进行滑动,而不能使view本身滑动,且非平滑,可使用Scroller有过渡滑动的效果。
  • Scroller实现滑动的具体过程:

    • 在MotionEvent.ACTION_UP事件触发时调用startScroll()方法,该方法并没有进行实际的滑动操作,而是记录滑动相关量
    • 马上调用invalidate/postInvalidate()方法,请求View重绘,导致View.draw方法被执行
    • 紧接着会调用View.computeScroll()方法,此方法是空实现,需要自己处理逻辑。具体逻辑是:先判断computeScrollOffset(),若为true(表示滚动未结束),则执行scrollTo()方法,它会再次调用postInvalidate(),如此反复执行,直到返回值为false。
    • 08.Android之View事件问题

8.0.1.4 谈一谈View的工作原理,执行流程,MeasureSpec是什么?有什么作用?

  • View工作流程

    • View工作流程简单来说就是,先measure测量,用于确定View的测量宽高,再 layout布局,用于确定View的最终宽高和四个顶点的位置,最后 draw绘制,用于将View 绘制到屏幕上。
    • 08.Android之View事件问题
    • ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。
    • View的绘制流程是从ViewRoot和performTraversals开始。
    • performTraversals()依次调用performMeasure()、performLayout()和performDraw()三个方法,分别完成顶级 View的绘制。
    • 其中,performMeasure()会调用measure(),measure()中又调用onMeasure(),实现对其所有子元素的measure过程,这样就完成了一次measure过程;接着子元素会重复父容器的measure过程,如此反复至完成整个View树的遍历。layout和draw同理。
  • MeasureSpec作用 技术博客大总结

    • 通过宽测量值widthMeasureSpec和高测量值heightMeasureSpec决定View的大小

      • MeasureSpec组成:一个32位int值,高2位代表SpecMode(测量模式),低30位代表SpecSize
      • 直接继承View的自定义View需要重写onMeasure()并设置wrap_content时的自身大小,否则效果相当于macth_parent
    • SpecMode有三类:

      • UNSPECIFIED 表示父容器不对View有任何限制,一般用于系统内部,表示一种测量状态;
      • EXACTLY 父容器已经检测出view所需的精确大小,这时候view的最终大小SpecSize所指定的值,相当于match_parent或指定具体数值。
      • AT_MOST 父容器指定一个可用大小即SpecSize,view的大小不能大于这个值,具体多大要看view的具体实现,相当于wrap_content。

8.0.1.6 SurfaceView和View的区别,说一下SurfaceView的工作原理,为何不会导致页面卡顿?

  • SurfaceView是从View基类中派生出来的显示类,他和View的区别有:

    • View需要在UI线程对画面进行刷新,而SurfaceView可在子线程进行页面的刷新
    • View适用于主动更新的情况,而SurfaceView适用于被动更新,如频繁刷新,这是因为如果使用View频繁刷新会阻塞主线程,导致界面卡顿 技术博客大总结
    • SurfaceView在底层已实现双缓冲机制,而View没有,因此SurfaceView更适用于需要频繁刷新、刷新时数据处理量很大的页面

关于其他内容介绍

01.关于博客汇总链接


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Eric Meyer on CSS

Eric Meyer on CSS

Eric Meyer / New Riders Press / 2002-7-8 / USD 55.00

There are several other books on the market that serve as in-depth technical guides or reference books for CSS. None, however, take a more hands-on approach and use practical examples to teach readers......一起来看看 《Eric Meyer on CSS》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换