View绘制流程学习总结

一个应用界面是一个Activity,通过一个实现了Window抽象类的PhoneWindow和DecorView相互关联,DecorView本质上是一个FrameLayout,他有一个唯一的子View,是LinearLayout,包含两个子View,TitleView和ContentView。我们在onCreate(Bundle bundle)方法中调用的setContentView就是填充的这个布局。Window是一个窗口的特别是独立绘制不影响其他界面,不会触发其他界面的输入事件。一个窗口独占一个Surface由WindowManagerService分配,可以把Surface看做一块画布,应用通过Canvas或OpenGL在其上面作画。窗口类包含三个核心组件:

1.WindowManager.LayoutParams:窗口的布局参数

2.Callback:窗口的回调接口,通常由Activity实现

3.ViewTree:窗口所承载的控件树

PhoneWindow:

我们调用setContentView()方法时,实际上就是完成了对所关联的PhoneWindow的ViewTree的设置。

seContentView()解析:

在Activity中调用此方法,实际上是调用了getWindow().setContentView()。getWindow()会返回Activity所关联的PhoneWindow。PhoneWindow的setContentView的源码:

1.会判断ContentView的父容器是否为null,为空的话调用installDecor()生成。

else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition
    // mContentParent不为null,则移除decorView的所有子View
    mContentParent.removeAllViews();
  }
  if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    // 开启了Transition,做相应的处理,我们不讨论这种情况
    // 感兴趣的同学可以参考源码
    . . .
  } else {
    // 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局
    // 填充布局也就是把我们设置的ContentView加入到mContentParent中
    mLayoutInflater.inflate(layoutResID, mContentParent);
  }

在setContentView中,一般会调用LayoutInflater.inflate(...,..)来填充布局。

源码如下:

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
  return inflate(resource, root, root != null);
}

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
  final Resources res = getContext().getResources();
  . . .
  final XmlResourceParser parser = res.getLayout(resource);
  try {
    return inflate(parser, root, attachToRoot);
  } finally {
    parser.close();
  }
}//这里的root指的是DectroView

最终调用inflate(XmlPullParser,ViewGroup,boolean)方法来填充布局。在此方法中会进行一些判断:比如读取开始标记,判断父容器是否为null,然后递归调用rInflate(parser,root,inflaterCoontext,attrs,false)填充布局。


ViewRoot

ViewRoot负责View的绘制,每个窗口的dectorView都有一个与之关联的ViewRoot对象,由WindowManager来维护。

在Activity启动时建立dectorView和ViewRoot的关联。

View绘制的起点是联系建立后调用requestLayout()方法,完成界面的初次布局,实际调用的是ViewRootImpl类的requestLayout()方法。在源码中首先检查是否为主线程,然后调用用scheduleTraversals()方法完成一次绘制流程。该方法会向主线程发送一个遍历的消息,最后导致ViewRootImll的performTraversals()方法被调用。


猜你喜欢

转载自blog.csdn.net/qq_38256015/article/details/80688626