View绘制

主要方法

  • Measure:测量计算控件大小
  • Layout:确定绘制位置
  • Draw:开始绘制

MeasureSpec

MeasureSpec由一个32位的整形组成,其中高2位是mode,后30位是size。可以通过MeasureSpec.getMode()MeasureSpec.getSize() 方法获得相应的值。

MeasureSpec有三种模式:

  • UNSPECIFIED:父容器对子容器没有限制,子容器要多大就多大
  • EXACTLY:父容器已经检测出View所需要的精确大小,这个时候View的大小就是SpecSIze所指定的值。他对应于LayoutParams中的match_parent和具体的数值这两种模式。
  • AT_MOST:父容器指定了一个可用大小SpecSize。View的大小不能超过这个值,它对应于LayoutParams的wrap_content。

View中measure()方法是final的,其中调用了onMeasure()方法,需要自定义测量可以覆写此方法。

Layout

Draw

invalidata():如有有变化才绘制 requestlayout():请求重新测量和确定位置,不绘制。

事件分发机制

Andorid中View是树形结构,触摸事件产生后需要重上到下分发事件,确定需要处理。

事件类型

事件指的是用户触摸产生的拦截机制,主要是在MotionEvent类中的几个常量,分别是:

  • ACTION_DOWN:按下
  • ACTION_UP:抬起
  • ACTION_MOVE:移动
  • ACTION_CANCEL:取消

事件分发流程以及主要涉及的方法

产生了一个MotionEvent之后,系统会将该事件传递给Activity -> ViewGroup -> View处理。

  • dispatchTouchEvent():表示是否消费了当前事件
  • onInterceptTpuchEvent():只存在ViewGroup中,判断是否需要拦截
  • onTouchEvent():真正对MotionEvent进行处理,在dispatchTouchEvent进行调用。
//三个方法之间的关系
public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean consume = false;//事件是否被消费
        if (onInterceptTouchEvent(ev)){//调用onInterceptTouchEvent判断是否拦截事件
            consume = onTouchEvent(ev);//如果拦截则调用自身的onTouchEvent方法
        }else{
            consume = child.dispatchTouchEvent(ev);//不拦截调用子View的dispatchTouchEvent方法
        }
        return consume;
    }

其中如果onInterceptTpuchEvent方法返回True,直接会调用onTouchEvent方法,不会向下传递事件。

如果都不拦截事件的话,完整流程是这样:

  • Acivity.dispatchTouchEvent() ->
  • ViewGroupA.onInterceptTpuchEvent() ->
  • ViewGroupB.onInterceptTpuchEvent() ->
  • View.onTouchEvent() ->
  • ViewGroupB.onTouchEvent() ->
  • ViewGroupA.onTouchEvent() ->
  • Acivity.onTouchEvent()

为了更好的理解,盗了网上的一张图

事件分发流程图

返回值

  • True:表示拦截此事件,不需要继续传递
  • False:不拦截,继续传递事件。

注意点

  • 如果设置了onTouchListener,那么OnTouchListener方法中的onTouch方法会被回调。onTouch方法返回true,则onTouchEvent方法不会被调用(onClick事件是在onTouchEvent中调用)所以三者优先级是onTouch->onTouchEvent->onClick