Android——UI(一):UI绘制流程

来源:互联网 发布:nginx和apache的关系 编辑:程序博客网 时间:2024/05/18 09:03

UI绘制流程

1、为什么调用setContentView之后就可以显示我们想要的布局?

进入setContentView方法可以看到

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

其中getWindow()返回了一个Window对象,Window是一个抽象类,Window中有很多抽象方法,最常见的就是我们经常写的findViewById。
Window.Java通过注释

/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
*

The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/

可以看到Window处于最顶层,并且在Android中只有PhoneWindow一个子类。

进入PhoneWindow的setContentView可以看到

    @Override    public void setContentView(int layoutResID) {        // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window        // decor, when theme attributes and the like are crystalized. Do not check the feature        // before this happens.        if (mContentParent == null) {            installDecor();        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {            mContentParent.removeAllViews();        }        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {        //Activity的转场动画            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                    getContext());            transitionTo(newScene);        } else {            mLayoutInflater.inflate(layoutResID, mContentParent);        }        mContentParent.requestApplyInsets();        final Callback cb = getCallback();        if (cb != null && !isDestroyed()) {            cb.onContentChanged();        }        mContentParentExplicitlySet = true;    }

首先是对mContentParent 进行判空,这个mContentParent 是一个ViewGroup,通过注释

    // This is the view in which the window contents are placed. It is either    // mDecor itself, or a child of mDecor where the contents go.

可以看到mContentParent 就是Window的内容,注释中的mDecor是DecorView,Android中最顶层的View。

往下看会看到mLayoutInflater.inflate(layoutResID, mContentParent);
将id设置到ViewGroup中,这里暂不做深入。

当mContentParent为空时会执行installDecor();
进去之后,找到

        if (mContentParent == null) {            mContentParent = generateLayout(mDecor);

继续查看generateLayout(mDecor)

TypedArray a = getWindowStyle();
        if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {            requestFeature(FEATURE_NO_TITLE);        } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {            // Don't allow an action bar if there is no title.            requestFeature(FEATURE_ACTION_BAR);        }

这里代码是不是很熟悉,在这里就会获取到Window的style。
继续往下看

 int layoutResource;        int features = getLocalFeatures();        // System.out.println("Features: 0x" + Integer.toHexString(features));        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {            layoutResource = R.layout.screen_swipe_dismiss;        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {            if (mIsFloating) {                TypedValue res = new TypedValue();                getContext().getTheme().resolveAttribute(                        R.attr.dialogTitleIconsDecorLayout, res, true);                layoutResource = res.resourceId;            } else {                layoutResource = R.layout.screen_title_icons;            }

在这里通过不同的features去加载不同的layout,同时也说明了,requestFeature必须在setContentView之前进行调用。

再来看layoutResource即将加载的布局是什么,在这里以R.layout.screen_simple为例,进去之后可以看到

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    android:orientation="vertical">    <ViewStub android:id="@+id/action_mode_bar_stub"              android:inflatedId="@+id/action_mode_bar"              android:layout="@layout/action_mode_bar"              android:layout_width="match_parent"              android:layout_height="wrap_content"              android:theme="?attr/actionBarTheme" />     //显示我们布局的地方    <FrameLayout         android:id="@android:id/content"         android:layout_width="match_parent"         android:layout_height="match_parent"         android:foregroundInsidePadding="false"         android:foregroundGravity="fill_horizontal|top"         android:foreground="?android:attr/windowContentOverlay" /></LinearLayout>

Activity加载UI-类图关系和视图结构

小结:

  • 每个Activity都有一个关联的Window抽象类,用来描述应用程序窗口。
  • 在Android中,Window有且仅有一个实现类PhoneWindow
  • PhoneWindow中包含了一个DecorView
  • 我们自己设置的布局在DecorView下的FrameLayout中
  • 最后通过mLayoutInflater.inflate将id与mContentParent进行关联

2、LayoutInflater如何把xml转成View?

  • 为什么include不能作为xml资源文件布局根节点?
  • 为什么merge只能作为xml资源文件布局根节点?

带着疑问继续看源码。

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {        final Resources res = getContext().getResources();        if (DEBUG) {            Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("                    + Integer.toHexString(resource) + ")");        }        //获取xml解析器        final XmlResourceParser parser = res.getLayout(resource);        try {        //使用解析器获取View            return inflate(parser, root, attachToRoot);        } finally {            parser.close();        }    }

进入inflate(parser, root, attachToRoot)继续看,

//获取xml中的属性集final AttributeSet attrs = Xml.asAttributeSet(parser);
    int type;    //获取根节点    while ((type = parser.next()) != XmlPullParser.START_TAG &&          type != XmlPullParser.END_DOCUMENT) {          // Empty                }
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, inflaterContext, attrs, false);         } else { final View temp = createViewFromTag(root, name, inflaterContext, attrs);       ViewGroup.LayoutParams params = null;   if (root != null) {   if (DEBUG) {     System.out.println("Creating params from root: " + root);                        }    // 获取xml上的属性,在自定义属性时经常用到     params = root.generateLayoutParams(attrs);    if (!attachToRoot) {    // 设置属性       temp.setLayoutParams(params);                        }                    }
// 解析完父容器之后继续解析子控件    rInflateChildren(parser, temp, attrs, true);
    final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,            boolean finishInflate) throws XmlPullParserException, IOException {        rInflate(parser, parent, parent.getContext(), attrs, finishInflate);    }
 void rInflate(XmlPullParser parser, View parent, Context context,            AttributeSet attrs, boolean finishInflate) 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_TAG.equals(name)) {                parseViewTag(parser, parent, attrs);            } else if (TAG_INCLUDE.equals(name)) {                //判断是否是跟标签                if (parser.getDepth() == 0) {                //include不能作为跟标签                    throw new InflateException("<include /> cannot be the root element");                }                parseInclude(parser, context, parent, attrs);            } else if (TAG_MERGE.equals(name)) {            //merge必须作为跟标签                throw new InflateException("<merge /> must be the root element");            } else {            //最终将子布局添加到父布局中                final View view = createViewFromTag(parent, name, context, attrs);                final ViewGroup viewGroup = (ViewGroup) parent;                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);                rInflateChildren(parser, view, attrs, true);                viewGroup.addView(view, params);            }        }        if (finishInflate) {            parent.onFinishInflate();        }    }

LayoutInflater

0 0