你需要知道的Android View的创建.docx

上传人:b****3 文档编号:24922189 上传时间:2023-06-02 格式:DOCX 页数:17 大小:194.40KB
下载 相关 举报
你需要知道的Android View的创建.docx_第1页
第1页 / 共17页
你需要知道的Android View的创建.docx_第2页
第2页 / 共17页
你需要知道的Android View的创建.docx_第3页
第3页 / 共17页
你需要知道的Android View的创建.docx_第4页
第4页 / 共17页
你需要知道的Android View的创建.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

你需要知道的Android View的创建.docx

《你需要知道的Android View的创建.docx》由会员分享,可在线阅读,更多相关《你需要知道的Android View的创建.docx(17页珍藏版)》请在冰豆网上搜索。

你需要知道的Android View的创建.docx

你需要知道的AndroidView的创建

你需要知道的AndroidView的创建

View的创建与绘制一向是很多人望而止步的问题。

然而我们在平常的应用开发中是最经常运用到的setContentView(),我们都会用在Activity的onCreate()的时候调用setContentView()来加载编辑好的XML布局。

但是实际上创建与绘制一个View,内部的实现方式的确比我们表面所编写的代码复杂得多,导致大家没能深入去了解View的创建与绘制。

接下来我们一步步来了解View的创建与绘制。

在研究setContentView()方法前,我们首先先看一下这图:

上图中,DecorView是个应用窗口的最顶层View。

(Decor的英文全称是Decoration,即“修饰”的意思)。

DecorView只有一个子元素是垂直LinearLayout。

在LinearLayout下有两个子布局,第一个是ViewStub,ViewStub就是ActionBar,它会根据theme判断有没使用ActionBar来决定是否引入ActionBar布局。

第二个是FrameLayout,这就是我们应用真实使用的父布局。

大家可以通过sdk工具HierarchyViewer来查看验证一下ViewTree的情况。

Window,中文解析“窗口”。

它是一个宏观的概念。

该类是一个抽象类,提供了绘制窗口的通用API,我们可以理解为它是一个载体。

接着我们看一下PhoneWindow,它是Android中Window的具体实现类。

PhoneWindow位于/frameworks/policies/base/phone/com/android/internal/policy/impl/PhoneWindow.java

PhoneWindow继承于Window类,我们可以通过实现具体抽象方法去绘制窗口,该类还包含DecorView内部类。

我们平时调用的setContentView()方法设置Activity的用户界面时,实际上是对PhoneWindow的ViewTree的设置。

我们通过一个比喻来理解他们之间的关系。

Window类相当于一幅画(抽象概念,什么画我们未知),PhoneWindow为一副齐白石先生的山水画(具体概念,我们知道了是谁的、什么性质的画),DecorView则为该山水画的具体内容(有山、有水、有树,各种界面)。

DecorView呈现在PhoneWindow上。

好了,有了这部分的认识之后,我们就开始从源码的角度去认识View的创建。

当我们自定义Activity继承Android.app.Activity时候,调用的setContentView()如下:

publicvoidsetContentView(@LayoutResintlayoutResID){

getWindow().setContentView(layoutResID);

initWindowDecorActionBar();

}

getWindow()方法返回一个PhoneWindow对象,那就是说调用的是PhoneWindow的setContentView()方法。

源码如下:

@Override

publicvoidsetContentView(intlayoutResID){

//Note:

FEATURE_CONTENT_TRANSITIONSmaybesetintheprocessofinstallingthewindow

//decor,whenthemeattributesandthelikearecrystalized.Donotcheckthefeature

//beforethishappens.

if(mContentParent==null){

//mContentParent即为上面提到的ContentView的父容器,若为空则调用installDecor()生成

installDecor();

}elseif(!

hasFeature(FEATURE_CONTENT_TRANSITIONS)){

//mContentParent不为null,则移除decorView的所有子View

mContentParent.removeAllViews();

}

if(hasFeature(FEATURE_CONTENT_TRANSITIONS)){

finalScenenewScene=Scene.getSceneForLayout(mContentParent,layoutResID,

getContext());

transitionTo(newScene);

}else{

//一般情况会来到这里,调用mLayoutInflater.inflate()方法来填充布局

//填充布局也就是把我们设置的ContentView加入到mContentParent中

mLayoutInflater.inflate(layoutResID,mContentParent);//2

}

finalCallbackcb=getCallback();

if(cb!

=null&&!

isDestroyed()){

//调用onContentChanged()回调方法通知Activity窗口内容发生了改变

cb.onContentChanged();

}

}

首先判断了mContentParent是否为null,如果为空则执行installDecor()方法,那么这个mContentParent又是什么呢?

我们看一下它的注释。

//Thisistheviewinwhichthewindowcontentsareplaced.Itiseither

//mDecoritself,orachildofmDecorwherethecontentsgo.

privateViewGroupmContentParent;

它是一个ViewGroup类型,结合2处代码,可以得知,这个mContentParent是我们设置的布局的父布局。

梳理下:

Activity通过PhoneWindow的setContentView方法来设置布局,而设置布局之前,会先判断是否存在mContentParent,而我们设置的布局文件则是mContentParent的子元素。

接着我们看一下installDecor(),我们看一下PhoneWindow#installDecor:

privatevoidinstallDecor(){

if(mDecor==null){

mDecor=generateDecor();//1

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

mDecor.setIsRootNamespace(true);

if(!

mInvalidatePanelMenuPosted&&mInvalidatePanelMenuFeatures!

=0){

mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);

}

}

if(mContentParent==null){

mContentParent=generateLayout(mDecor);//2

...

}

}

}

首先,会执行1处代码,调用PhoneWindow#generateDecor方法:

protectedDecorViewgenerateDecor(){

returnnewDecorView(getContext(),-1);

}

可以看出,这里实例化了DecorView,而DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout,由此可知它也是一个ViewGroup。

DecorView上面我们已经对它进行过一次解析了,大家可以返回去回顾下。

接下来我们看2处PhoneWindow#generateLayout方法的代码:

protectedViewGroupgenerateLayout(DecorViewdecor){

//Applydatafromcurrenttheme.

//从主题文件中获取样式信息

//加载TitleBar方法一

TypedArraya=getWindowStyle();

...

if(a.getBoolean(R.styleable.Window_windowNoTitle,false)){

requestFeature(FEATURE_NO_TITLE);

}elseif(a.getBoolean(R.styleable.Window_windowActionBar,false)){

//Don'tallowanactionbarifthereisnotitle.

requestFeature(FEATURE_ACTION_BAR);

}

if(...){

...

}

//Inflatethewindowdecor.

//加载窗口布局

intlayoutResource;

//加载TitleBar方法二

intfeatures=getLocalFeatures();

//System.out.println("Features:

0x"+Integer.toHexString(features));

if((features&(1<

=0){

layoutResource=R.layout.screen_swipe_dismiss;

}elseif(...){

...

}

Viewin=mLayoutInflater.inflate(layoutResource,null);//加载layoutResource

decor.addView(in,newViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));//往DecorView中添加子View,即mContentParent

mContentRoot=(ViewGroup)in;

ViewGroupcontentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);//这里获取的就是mContentParent

if(contentParent==null){

thrownewRuntimeException("Windowcouldn'tfindcontentcontainerview");

}

if((features&(1<

=0){

ProgressBarprogress=getCircularProgressBar(false);

if(progress!

=null){

progress.setIndeterminate(true);

}

}

if((features&(1<

=0){

registerSwipeCallbacks();

}

//Remainingsetup--ofbackgroundandtitle--thatonlyapplies

//totop-levelwindows.

...

returncontentParent;

}

由以上代码可以看出,该方法还是做了相当多的工作的,首先根据设置的主题样式来设置DecorView的风格,比如说有没有titlebar之类的,就是解析我们为Activity设置theme的地方,至于Theme的设置:

1.我们可以在AndroidManifest里面进行设置,为我们的Activity配置相应属性,即android:

theme=”“,PhoneWindow对象调用getWindowStyle()方法获取值。

2.也可以在setContentView()前调用requestFeature,指定requestFeature()指定窗口修饰符,PhoneWindow对象调用getLocalFeature()方法获取值;

对Theme操作完,我们才对layoutResource赋值的,因此我相信有不少人都曾经遇到一个错误——“requestFeature()mustbecalledbeforeaddingcontent”。

接着通过对features和mIsFloating的判断,设置窗口的风格修饰为layoutResource进行赋值。

得到了layoutResource以后,通过LayoutInflater.inflate()方法生成View对象。

并加入到decor中。

这就是为DecorView添加子View,而这里的子View则是上面提到的mContentParent。

如果上面设置了FEATURE_NO_ACTIONBAR,那么DecorView就只有mContentParent一个子View,这也解释了mContentParent对象注释:

mContentParent是DecorView本身或者是DecorView的一个子元素。

小结:

DecorView是顶级View,内部有titlebar和contentParent两个子元素,而内部根据theme设置TitleBar,和选择系统中的布局文件,将布局文件通过inflate转化为view,加入到mDecor中;这些布局文件中都包含一个id为content的FrameLayout,将其引用返回给mContentParent。

了解完PhoneWindow#installDecor后我们接着PhoneWindow#setContentView(),看到那部分2处代码:

mLayoutInflater.inflate(layoutResID,mContentParent);相信LayoutInflater大家跟setContentView()一样常用。

因为在一些动态加载View和BaseAdapter适配器的代码编写中我们都会用到。

我们来看一下它的代码:

publicViewinflate(XmlPullParserparser,@NullableViewGrouproot){

returninflate(parser,root,root!

=null);

}

publicViewinflate(@LayoutResintresource,@NullableViewGrouproot,booleanattachToRoot){

finalResourcesres=getContext().getResources();

if(DEBUG){

Log.d(TAG,"INFLATINGfromresource:

\""+res.getResourceName(resource)+"\"("

+Integer.toHexString(resource)+")");

}

finalXmlResourceParserparser=res.getLayout(resource);

try{

returninflate(parser,root,attachToRoot);

}ally{

parser.close();

}

}

LayoutInflater.inflate()将上面创建的decorView作为root的参数。

上面的代码比较简单,重点是return那行的inflate(),我们看一下里面的实现过程:

publicViewinflate(XmlPullParserparser,@NullableViewGrouproot,booleanattachToRoot){

synchronized(mConstructorArgs){

Trace.traceBegin(Trace.TRACE_TAG_VIEW,"inflate");

finalContextinflaterContext=mContext;

finalAttributeSetattrs=Xml.asAttributeSet(parser);

ContextlastContext=(Context)mConstructorArgs[0];

mConstructorArgs[0]=inflaterContext;

Viewresult=root;

try{

//Lookfortherootnode.

inttype;

//一直读取xml文件,直到遇到开始标记

while((type=parser.next())!

=XmlPullParser.START_TAG&&

type!

=XmlPullParser.END_DOCUMENT){

//Empty

}

if(type!

=XmlPullParser.START_TAG){

thrownewInflateException(parser.getPositionDescription()

+":

Nostarttagfound!

");

}

finalStringname=parser.getName();

if(DEBUG){

System.out.println("**************************");

System.out.println("Creatingrootview:

"

+name);

System.out.println("**************************");

}

//单独处理标签

if(TAG_MERGE.equals(name)){

if(root==null||!

attachToRoot){

thrownewInflateException("canbeusedonlywithavalid"

+"ViewGrouprootandattachToRoot=true");

}

//递归地填充布局

rInflate(parser,root,inflaterContext,attrs,false);

}else{

//Tempistherootviewthatwasfoundinthexml

//能在XML发现根View

finalViewtemp=createViewFromTag(root,name,inflaterContext,attrs);

ViewGroup.LayoutParamsparams=null;

if(root!

=null){

if(DEBUG){

System.out.println("Creatingparamsfromroot:

"+

root);

}

//Createlayoutparamsthatmatchroot,ifsupplied

//获取父容器的布局参数(LayoutParams)

params=root.generateLayoutParams(attrs);

if(!

attachToRoot){

//Setthelayoutparamsfortempifwearenot

//attaching.(Ifweare,weuseaddView,below)

//若attachToRoot参数为false,则我们只会将父容器的布局参数设置给根View

temp.setLayoutParams(params);

}

}

if(DEBUG){

System.out.println("----->startinflatingchildren");

}

//Inflateallchildrenundertempagainstitscontext.

//递归加载根View的所有子View

rInflateChildren(parser,temp,attrs,true);

if(DEBUG){

System.out.println("----->doneinflatingchildren");

}

//Wearesupposedtoattachalltheviewswefound(inttemp)

//toroot.Dothatnow.

//若父容器不为空且attachToRoot为true,则将父容器作为根View的父View包裹上来

if(root!

=null&&attachToRoot){

root.addView(temp,params);

}

//Decidewhethertoreturntherootthatwaspassedinorthe

//topviewndinxml.

//若root为空或是attachToRoot为false,则以根View作为返回值

if(root==null||!

attachToRoot){

result=temp;

}

}

}catch(XmlPullParserExceptione){

InflateExceptionex=newInflateException(e.getMessage());

ex.initCause(e);

throwex;

}catch(Exceptione){

InflateExceptionex=newInflateException(

parser.getPositionDescription()

+":

"+e.getMessage());

ex.initCause(e);

throwex;

}finally{

//Don'tretainstaticreferenceoncontext.

mC

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学 > 物理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1