简介

我们在学习android的时候,直接在xml中指定android的标签,就能显现出各式各样的界面,但是我们并不了解其中的绘制流程,从而抱着一个学习的心态,从源码角度去查看view的绘制机制。

目录

1.view的绘制流程(一)
2.总结

view的绘制流程

在进行分析之前,我们可以先看看下面的流程图:

每个Activity都持有Window的对象,Android 为 Window 提供了唯一的实现类 PhoneWindow。PhoneWindow 终究是 Window,它并不具备多少 View 相关的能力,不过 PhoneWindow 中持有一个 Android 中非常重要的一个 View 对象 Decor(装饰)View,它在 PhoneWindow 中的定义如下:

public class PhoneWindow extends Window{  // 最顶级view的节点,起到一个window装饰的根组件  private DecorView mDecor; }

查看 DecorView 继承关系得知,DecorView 继承自 FrameLayout。

public class DecorView extends FrameLayout {}

其中DecorView 的子view是一个LinerLayout布局包含TitleView和ContentView,ContentView的子布局就是FrameLayout。我们平常在Activity中的onCreate()方法中,setContentView(int layoutResID)所传入的布局就是影响着ContentView。

public void setContentView(@LayoutRes int layoutResID) {      getWindow().setContentView(layoutResID);      . . .}

其中getWindow()就是PhoneWindow对象,其中的setContentView()方法,源码如下:

@Overridepublic void setContentView(int layoutResID) {// mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成  if (mContentParent == null) {       installDecor();  } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {    // 具有FEATURE_CONTENT_TRANSITIONS特性表示开启了Transition    // mContentParent不为null,则移除decorView的所有子View    mContentParent.removeAllViews();  }  if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);  } else {    // 一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局    mLayoutInflater.inflate(layoutResID, mContentParent);  }  . . .  // cb即为该Window所关联的Activity  final Callback cb = getCallback();  if (cb != null && !isDestroyed()) {    // 调用onContentChanged()回调方法通知Activity窗口内容发生了改变    cb.onContentChanged();  }  . . .}

我们主要看mLayoutInflater.inflate(layoutResID, mContentParent):

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();  }}public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {      synchronized (mConstructorArgs) {          final AttributeSet attrs = Xml.asAttributeSet(parser);          mConstructorArgs[0] = mContext;          View result = root;          try {              int type;              while ((type = parser.next()) != XmlPullParser.START_TAG &&                      type != XmlPullParser.END_DOCUMENT) {              }              if (type != XmlPullParser.START_TAG) {                  throw new InflateException(parser.getPositionDescription()                          + ": No start tag found!");              }              final String name = parser.getName();              if (TAG_MERGE.equals(name)) {                  if (root == null || !attachToRoot) {                      throw new InflateException("merge can be used only with a valid "                              + "ViewGroup root and attachToRoot=true");                  }                  rInflate(parser, root, attrs);              } else {                  View temp = createViewFromTag(name, attrs);                  ViewGroup.LayoutParams params = null;                  if (root != null) {                      params = root.generateLayoutParams(attrs);                      if (!attachToRoot) {                          temp.setLayoutParams(params);                      }                  }                  rInflate(parser, temp, attrs);                  if (root != null && attachToRoot) {                      root.addView(temp, params);                  }                  if (root == null || !attachToRoot) {                      result = temp;                  }              }          } catch (XmlPullParserException e) {              InflateException ex = new InflateException(e.getMessage());              ex.initCause(e);              throw ex;          } catch (IOException e) {              InflateException ex = new InflateException(                      parser.getPositionDescription()                      + ": " + e.getMessage());              ex.initCause(e);              throw ex;          }          return result;      }  }  

从这里我们就可以清楚地看出,LayoutInflater其实就是使用Android提供的pull解析方式来解析布局文件的。我们可以注意到createViewFromTag()这个方法,把节点名和参数传了进去。看到这个方法名,我们就应该能猜到,它是用于根据节点名来创建View对象的。在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。
这样我们就把view的根节点给创建出来了接下来我们继续查看rInflate(parser, temp, attrs):

private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)          throws XmlPullParserException, IOException {      final int depth = parser.getDepth();      int type;      while (((type = parser.next()) != XmlPullParser.END_TAG ||              parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {          if (type != XmlPullParser.START_TAG) {              continue;          }          final String name = parser.getName();          if (TAG_REQUEST_FOCUS.equals(name)) {              parseRequestFocus(parser, parent);          } else if (TAG_INCLUDE.equals(name)) {              if (parser.getDepth() == 0) {                  throw new InflateException(" cannot be the root element");              }              parseInclude(parser, parent, attrs);          } else if (TAG_MERGE.equals(name)) {              throw new InflateException(" must be the root element");          } else {              final View view = createViewFromTag(name, attrs);              final ViewGroup viewGroup = (ViewGroup) parent;              final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);              rInflate(parser, view, attrs);              viewGroup.addView(view, params);          }      }      parent.onFinishInflate();  }  

总结

可以看到,同样是createViewFromTag()方法来创建View的实例,然后递归调用rInflate()方法来查找这个View下的子元素,每次递归完成后则将这个View添加到父布局当中。这样整个布局文件都解析完成后就形成了一个完整的DOM结构,最终会把最顶层的根布局返回,至此inflate()过程全部结束。

我们继续学习view的绘制机制(二)

更多相关文章

  1. 手把手带你搭建 Elasticsearch 集群
  2. Android基础知识(2)—事件处理
  3. Intent介绍及Intent在Activity中的使用方法
  4. Android高手秘笈之View的绘制你应该知道的一切
  5. Android(安卓)弹无虚发之第五弹:来点儿不一样的Toast(自定义Toast,
  6. Android(安卓)adt v22.6.2 自动创建 appcompat_v7 解决方法,最低
  7. 关于Android的面试题目汇总
  8. Android(安卓)学习
  9. Android前台画面和后台service之间通信的方法之Broadcast(转)

随机推荐

  1. android 布局简括
  2. Android 异步加载图片
  3. Android中attr自定义属性详解
  4. android 注册、登录实现程序
  5. Failed to fetch URL https://dl-ssl.goo
  6. 2、Android应用程序基本特性
  7. android 7.0 ,6.0本地多语言切换失效
  8. 小技巧及总结
  9. Android简单注册表单
  10. Android studio 遇到Android SDK : Error