Activity如何将布局文件添加Windows窗口
2018-01-28 22:35
435 查看
一.首先了解几个概念
1.Window和PhoneWindow
一个顶级窗口查看和行为的一个抽象基类。这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法,比如添加背景,标题等等。当你需要用到Window的时候,你应该使用它的唯一实现类PhoneWindow。可以看到,Window是一个抽象基类,它提供了一系列窗口的方法,比如设置背景,标题等等,而它的唯一实现类则是PhoneWindow,每一个Activity上面都有一个Window,可以通过getWindow获取;
2.DecorView
在PhoneWindow里面,出现了成员变量DecorView的而这里,DecorView则是PhoneWindow里面的一个内部类,它是继承与FrameLayout,标题栏,内容栏,顶级上看是加载在DecorView上的。而DecorView则是由PhoneWindow负责添加
二.接下来我们通过源码分析Activity是如何通过setContentView()将布局文件添加到窗口上的
首先找到Activity的setContentView()方法
通过源码可以看出这里通过getWindow()获取window对象,再调用Window的setContentView()方法
而Window的setContentView()方法是个抽象方法,这时候我们找到Window的实现类PhoneWindow中的setContentView()方法
其中ViewGroup mContentParent则是我们Activity布局文件的父布局,首先判断mContentParent是否存在,如果不存在则通过installDecor()方法来创建该ViewGroup
在创建mContentParent的时候首先判断mDecor是否已经创建,如果没有创建则通过generateDecor()方法创建mDecor,接着判断mContentParent是否创建,没有创建则通过generateLayout(mDecor)方法获取到mContentParent(是DecorView中布局id为content的Framlayout)
布局screen_simple.xml的代码
从以上源码中可以看出requestFeature()为什么要在setContentView()之前调用,如果requestFeature()在setContentView()后调用则不能设置window属性
以上mContentParent已经创建完成 接下来看源码中如何通过LayoutInflater的inflate()方法将Activity中的布局文件添加到mContentParent中。
以下是LayoutInflater的inflate()方法
从以上源码中可以看出通过XmlPullParser来解析xml根据不同标签来创建不同的view,同时我们知道marge为什么只能用于根布局,include则不能作为根布局
1.Window和PhoneWindow
一个顶级窗口查看和行为的一个抽象基类。这个类的实例作为一个顶级View添加到Window Manager。它提供了一套标准的UI方法,比如添加背景,标题等等。当你需要用到Window的时候,你应该使用它的唯一实现类PhoneWindow。可以看到,Window是一个抽象基类,它提供了一系列窗口的方法,比如设置背景,标题等等,而它的唯一实现类则是PhoneWindow,每一个Activity上面都有一个Window,可以通过getWindow获取;
2.DecorView
在PhoneWindow里面,出现了成员变量DecorView的而这里,DecorView则是PhoneWindow里面的一个内部类,它是继承与FrameLayout,标题栏,内容栏,顶级上看是加载在DecorView上的。而DecorView则是由PhoneWindow负责添加
二.接下来我们通过源码分析Activity是如何通过setContentView()将布局文件添加到窗口上的
首先找到Activity的setContentView()方法
/** * Set the activity content from a layout resource. The resource will be * inflated, adding all top-level views to the activity. * * @param layoutResID Resource ID to be inflated. * * @see #setContentView(android.view.View) * @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams) */ public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
通过源码可以看出这里通过getWindow()获取window对象,再调用Window的setContentView()方法
/** * Convenience for * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)} * to set the screen content from a layout resource. The resource will be * inflated, adding all top-level views to the screen. * * @param layoutResID Resource ID to be inflated. * @see #setContentView(View, android.view.ViewGroup.LayoutParams) */ public abstract void setContentView(@LayoutRes int layoutResID);
而Window的setContentView()方法是个抽象方法,这时候我们找到Window的实现类PhoneWindow中的setContentView()方法
/** * The ID that the main layout in the XML layout file should have. */ public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; // This is the top-level view of the window, containing the window decor. private DecorView mDecor; // 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. ViewGroup mContentParent; @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)) { 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; }
其中ViewGroup mContentParent则是我们Activity布局文件的父布局,首先判断mContentParent是否存在,如果不存在则通过installDecor()方法来创建该ViewGroup
private void installDecor() { ............. if (mDecor == null) { mDecor = generateDecor(-1); .........以下代码省略 } else { ........... } if (mContentParent == null) { mContentParent = generateLayout(mDecor); .........以下代码省略 } }
在创建mContentParent的时候首先判断mDecor是否已经创建,如果没有创建则通过generateDecor()方法创建mDecor,接着判断mContentParent是否创建,没有创建则通过generateLayout(mDecor)方法获取到mContentParent(是DecorView中布局id为content的Framlayout)
protected ViewGroup generateLayout(DecorView decor) { ........................以上省略 //设置标题 状态栏 全屏等属性 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); } .................................. if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) { setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowTranslucentStatus, false)) { setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS & (~getForcedWindowFlags())); } if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation, false)) { setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION & (~getForcedWindowFlags())); } //获取设置的window属性 WindowManager.LayoutParams params = getAttributes(); // Inflate the window decor. 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; setCloseOnSwipeEnabled(true); } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = R.layout.screen_simple_overlay_action_mode; } ........ else { // Embedded, so no decoration is needed. layoutResource = R.layout.screen_simple; // System.out.println("Simple!"); } //加载资源id为layoutResource的布局并将其添加到DecorView中 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); //找到DecorView中R.id.content的FrameLayout作为Activity中布局的父布局 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); if (contentParent == null) { throw new RuntimeException("Window couldn't find content container view"); } return contentParent; }
布局screen_simple.xml的代码
<?xml version="1.0" encoding="utf-8"?> <!-- /* //device/apps/common/assets/res/layout/screen_simple.xml ** ** Copyright 2006, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. ** You may obtain a copy of the License at ** ** http://www.apache.org/licenses/LICENSE-2.0 ** ** Unless required by applicable law or agreed to in writing, software ** distributed under the License is distributed on an "AS IS" BASIS, ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ** See the License for the specific language governing permissions and ** limitations under the License. */ This is an optimized layout for a screen, with the minimum set of features enabled. --> <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>
从以上源码中可以看出requestFeature()为什么要在setContentView()之前调用,如果requestFeature()在setContentView()后调用则不能设置window属性
以上mContentParent已经创建完成 接下来看源码中如何通过LayoutInflater的inflate()方法将Activity中的布局文件添加到mContentParent中。
以下是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(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
public View inflate(XmlPullParser parser, @Nullable ViewGroup root, boolean attachToRoot) { synchronized (mConstructorArgs) { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "inflate"); final Context inflaterContext = mContext; final AttributeSet attrs = Xml.asAttributeSet(parser); Context lastContext = (Context) mConstructorArgs[0]; mConstructorArgs[0] = inflaterContext; //root为mContentParent View result = root; try { // Look for the root node. int type; while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { // Empty } if (type != XmlPullParser.START_TAG) { throw new InflateException(parser.getPositionDescription() + ": No start tag found!"); } final String name = parser.getName(); if (DEBUG) { System.out.println("**************************"); System.out.println("Creating root view: " + name); System.out.println("**************************"); } //判断是否是marge标签 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 { // Temp is the root view that was found in the xml final View temp = createViewFromTag(root, name, inflaterContext, attrs); ViewGroup.LayoutParams params = null; if (root != null) { // Create layout params that match root, if supplied params = root.generateLayoutParams(attrs); } // Inflate all children under temp against its context. rInflateChildren(parser, temp, attrs, true); // We are supposed to attach all the views we found (int temp) // to root. Do that now. if (root != null && attachToRoot) { root.addView(temp, params); } .................. } } catch (XmlPullParserException e) { final InflateException ie = new InflateException(e.getMessage(), e); ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } catch (Exception e) { final InflateException ie = new InflateException(parser.getPositionDescription() + ": " + e.getMessage(), e); ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; mConstructorArgs[1] = null; Trace.traceEnd(Trace.TRACE_TAG_VIEW); } return result; } }
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; boolean pendingRequestFocus = false; 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)) { pendingRequestFocus = true; consumeChildElements(parser); } else if (TAG_TAG.equals(name)) { parseViewTag(parser, parent, attrs); //判断是否是include标签 } else if (TAG_INCLUDE.equals(name)) { if (parser.getDepth() == 0) { throw new InflateException("<include /> cannot be the root element"); } parseInclude(parser, context, parent, attrs); //判断是否是marge标签 } else if (TAG_MERGE.equals(name)) { 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 (pendingRequestFocus) { parent.restoreDefaultFocus(); } if (finishInflate) { parent.onFinishInflate(); } }
从以上源码中可以看出通过XmlPullParser来解析xml根据不同标签来创建不同的view,同时我们知道marge为什么只能用于根布局,include则不能作为根布局
相关文章推荐
- 如何把Activity的布局文件添加至窗口
- android中如果activity中应用fragementlayout布局文件,如何给其中的控件添加事件监听。
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- android中布局文件添加到窗口流程分析
- android的布局xml文件如何添加注释?
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- 请问,如何在状态机工作流设计时将StateActivity、EventDrivntActivity、SetStateActivity作为一个整体添加到设计布局上呢?
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起
- C# 如何在 Windows 文件系统中添加自定义右键菜单
- Android中将布局文件/View添加至窗口过程分析
- Android : 如何在布局文件中添加自己的组件
- 【转载】Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起