Android中的 View绘制流程及事件分发

1.先盗用一张图说一下android的窗口结构:

Activity:可以看做是人与Android机器交互的窗口(就是我们工程中的那个Activity)。

  • PhoneWindow:可以看做是Activity与View之间交互的桥梁,Activity通过PhoneWindow与View进行交互,每一个Activity都有一个对应的PhoneWindow。该类继承自Window类,它内部包含了一个DecorView对象。PhoneWindow将DecorView对象进行一定的包装并作为应用窗口的跟View,并且提供通用的窗口操作接口。
  • DecorView:PhoneWindow类的内部类,将要显示的View呈现在PhoneWindow上,它是所有应用窗口的根View。它只有一个子元素即一个垂直的LinearLayout。在LinearLayout内部有两个子元素,一个是TitleView(标题)另一个是ContentView(一个FrameLayout),Activity中的setContentView就是设置这个ContentView,而在设置ContentView之前可以通过设置requestWindowFeature(Window.FEATURE_NO_TITLE)取消掉标题。
  • 当Activity创建时会在onCreate()方法中调用setContentView()方法,但是我们需要明确的一点就是此方法只是完成了对ContentView的创建,而并没有去执行对View的绘制。
  • 那么是谁去负责执行对View绘制的整个流程呢?答案是ViewRoot,每个DecorView都有一个与之对应的ViewRoot,在Activtiy启动时,ActivityFThread.handleResumeActivity()方法里建立了两者的关联,两者建立关联后ViewRoot类的requestLayout()方法会被执行,用来建立用户界面的初次布局,此方法实际上是ViewRootImpl类中的requestLayout()方法
    @Override
    public void requestLayout() {
      if (!mHandlingLayoutInLayoutRequest) { 
        checkThread();//是否在主线程
        mLayoutRequested = true;
        scheduleTraversals();//调用performTraversals()方法
      }
    }
    

接下来就进入到了我们日常自定义View中需要用到的三个阶段了                                                                                        measure(测量大小)                                                                                                                                                         

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
  . . . 
}

layout(判断位置)                                                                                                                                                             

public void layout(int l, int t, int r, int b) {
    // l为本View左边缘与父View左边缘的距离
    // t为本View上边缘与父View上边缘的距离
    // r为本View右边缘与父View左边缘的距离
    // b为本View下边缘与父View上边缘的距离
    ...
}

draw(绘制)                                                                                                                                                                                 

public void draw(Canvas canvas) {
  . . . 
}

当View.draw(Canvas)方法执行完毕后,整个View的绘制流程也就基本完成了。

2.View的事件分发机制

  • 在Android中MotionEvent对象是专门为我们在屏幕上的点击、滑动等一系列动作负责的,不管我们在屏幕上进行什么样的操作,无外乎是:
  1. 点击(ACTION_DOWN) -----   抬起(ACTION_UP
  2. 或者点击(ACTION_DOWN)---- 滑动(ACTION_MOVE)----  抬起(ACTION_UP)这两种情况
  • 一个MotionEvent产生了之后,系统会通过三个比较重要的方法把这个MotionEvent分发给一个具体的View
  1. public boolean dispatchTouchEvent(MotionEvent event)                                                                                                    它的返回值是一个Boolean值,返回true表示事件被消费,那么此次事件的传递也就会终止了,返回false表示View以及子View均没有消费事件,将调用父View的onTouchEvent()方法。如果一个事件传递给了View,那么此方法一定会被调用。
  2. public boolean onInterceptTouchEvent(MotionEvent ev)                                                                                                   此方法是一个事件拦截方法,只有在ViewGroup里面才有,View中是没有此方法的。它的返回值也是个Boolean值,返回true表示拦截了事件,拦截之后会调用onTouchEvent()方法,事件不会再向下分发。返回false表示不拦截,事件会继续向下分发,调用子View的dispatchTouchEvent(MotionEvent event)方法。
  3. public boolean onTouchEvent(MotionEvent ev)                                                                                                                   此方法在dispatchTouchEvent(MotionEvent event)  中调用,返回true表示事件消费,终止分发,返回false表示此次事件不消费,调用父View的onTouchEvent(MotionEvent event)  方法.

来张图给大家说明一下:

        简单点表达就是产生点击事件,父ViewGroup通过dispatchTouchEvent进行分发,调用onInterceptTouchEvent是否进行拦截,拦截则调用本身的onTouchEvent方法进行消费,消费事件结束,不消费向上传递,直到调用Activity的onTouchEvent方法结束,不拦截则调用子View的dispatchTouchEvent方法继续分发。

        对于事件的分发就简单的描述到这里,也算是自己简单的做个笔记,欢迎大家指正

猜你喜欢

转载自blog.csdn.net/zhao_Android/article/details/82997501
今日推荐