Android视图加载到窗体的流程分析
2016-06-26 23:31
435 查看
Android视图加载到窗体的流程分析
ONE Goal ,ONE Passion !
每天与界面打交道,也没有真正去整理过activity的界面时如何加载到窗体上的.扒扒代码看看系统是如何帮我们做的吧.
想知道布局视图加载到手机屏幕上的流程,我们首先要从acticity中的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().setContentView(layoutResID);
2.1:那么getWindow()到底是什么鬼.
/** * Retrieve the current {@link android.view.Window} for the activity. * This can be used to directly access parts of the Window API that * are not available through Activity/Screen. * * @return Window The current window, or null if the activity is not * visual. */ private Window mWindow; public Window getWindow() { return mWindow; }
2.2:咦.mWindow怎么来的呢?Activit中的main方法中会调用attach.随之就产出了一个mWindow对象.我们的mWindow对象实际上是一个PhoneWindow.
final void attach(...){ mWindow = new PhoneWindow(this); mWindow.setCallback(this); mWindow.setOnWindowDismissedCallback(this); mWindow.getLayoutInflater().setPrivateFactory(this); .... }
2.3: 拿到了mWindow后,看看它有做了什么
/** * 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) */ 将一个布局资源文件中设置为screen content(屏幕内容),这个资源将被填充到这个screen的顶层view中. public abstract void setContentView(@LayoutRes int layoutResID);
2.4可以看到getWindow().setContentView(layoutResID);实际上是PhoneWindow.setContentWindow.
PhoneWindow中的setContentWindow();public void setContentView(int layoutResID) { if (mContentParent == null) { installDecor(); } else { mContentParent.removeAllViews(); } mLayoutInflater.inflate(layoutResID, mContentParent);// 将layoutResID填充,他的父View是mContentParent是在installDecor方法里面mContentParent = generateLayout(mDecor);得到的 final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }
执行到installDecor()
private void installDecor() { if (mDecor == null) { mDecor = generateDecor();//生成装饰窗口,装饰窗口继承自FrameLayout mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } } if (mContentParent == null) { mContentParent = generateLayout(mDecor);// 产生布局,返回父布局,暂时这样理解,具体进去看代码 mDecor.makeOptionalFitsSystemWindows(); mTitleView = (TextView)findViewById(com.android.internal.R.id.title); ...... } } }
generateLayout的代码如下
protected ViewGroup generateLayout(DecorView decor) { // Apply data from current theme. TypedArray a = getWindowStyle();// 获取当前设置的主题 ...... if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) { requestFeature(FEATURE_NO_TITLE);//可以看到平时在AndroidManifest配置的窗口等各其实在代码里都是在这里修改的 } else if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBar, false)) { // Don't allow an action bar if there is no title. requestFeature(FEATURE_ACTION_BAR); } if (a.getBoolean(com.android.internal.R.styleable.Window_windowActionBarOverlay, false)) { requestFeature(FEATURE_ACTION_BAR_OVERLAY); } //19-63行根据我们指定的有无标题等各种窗口风格得到对应的默认布局, //这些布局在android4.2-source\frameworks\base\core\res\res\layout int layoutResource; int features = getLocalFeatures(); if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_title_icons; } removeFeature(FEATURE_ACTION_BAR); } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { layoutResource = com.android.internal.R.layout.screen_progress; } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true); layoutResource = res.resourceId; } else { layoutResource = com.android.internal.R.layout.screen_custom_title; } removeFeature(FEATURE_ACTION_BAR); } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { if (mIsFloating) { TypedValue res = new TypedValue(); getContext().getTheme().resolveAttribute( com.android.internal.R.attr.dialogTitleDecorLayout, res, true); layoutResource = res.resourceId; } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { if ((features & (1 << FEATURE_ACTION_BAR_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_action_bar_overlay; } else { layoutResource = com.android.internal.R.layout.screen_action_bar; } } else { layoutResource = com.android.internal.R.layout.screen_title; } } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode; } else { layoutResource = com.android.internal.R.layout.screen_simple; } mDecor.startChanging(); View in = mLayoutInflater.inflate(layoutResource, null);//根据上面的判断选择的layoutResource填充成View decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));//调用装饰窗口的addView方法将上一步生成的View添加到最外层的装饰窗口 ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);//ID_ANDROID_CONTENT对应的都是@android:id/content其实是一个FrameLayout ...... mDecor.finishChanging(); return contentParent; }
这里已经完成了对布局文件的加载.
第二:再看看initWindowDecorActionBar();
/** * Creates a new ActionBar, locates the inflated ActionBarView, * initializes the ActionBar with the view, and sets mActionBar. */ 创建一个新的ActionBar,去放置在ActionBarView中.将ActionBar初始化一个view. private void initWindowDecorActionBar() { Window window = getWindow(); // Initializing the window decor can change window feature flags. // Make sure that we have the correct set before performing the test below. window.getDecorView(); if (isChild() || !window.hasFeature(Window.FEATURE_ACTION_BAR) || mActionBar != null) { return; } mActionBar = new WindowDecorActionBar(this); mActionBar.setDefaultDisplayHomeAsUpEnabled(mEnableDefaultActionBarUp); mWindow.setDefaultIcon(mActivityInfo.getIconResource()); mWindow.setDefaultLogo(mActivityInfo.getLogoResource()); }
第三:看一下screen两种常见布局:
默认screen布局:android4.2-source\frameworks\base\core\res\res\layout文件夹下Screen-title.xml
<linearlayout android:fitssystemwindows="true" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Popout bar for action modes --> <viewstub android:id="@+id/action_mode_bar_stub" android:inflatedid="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_height="wrap_content" android:layout_width="match_parent"> <frameLayout android:layout_width="match_parent" android:layout_height="?android:attr/windowTitleSize" style="?android:attr/windowTitleBackgroundStyle"> <textview android:background="@null" android:fadingedge="horizontal" android:gravity="center_vertical" android:id="@android:id/title" android:layout_height="match_parent" android:layout_width="match_parent" style="?android:attr/windowTitleStyle"> </frameLayout> <frameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> </viewstub></linearlayout>
设置全屏时加载的screen布局
<linearlayout android:fitssystemwindows="true" android:layout_height="match_parent" android:layout_width="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <viewstub android:id="@+id/action_mode_bar_stub" android:inflatedid="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_height="wrap_content" android:layout_width="match_parent"> <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" /> </viewstub></linearlayout>
第4: 我们设置布局时使用的setConentView,为什么是setContent呢?其实就是将资源文件加载到了content中.
最后总结一下:我们设置setContentView时关键就是执行了下面两行代码
//将布局填充到Content中 getWindow().setContentView(layoutResID); //创建 ActionBar. initWindowDecorActionBar();
要睡觉了.(~﹃~)~zZ
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories