当前位置: 首页 > 后端技术 > Java

Android中Activity的setContentView方法解析

时间:2023-04-01 20:02:01 Java

PS:本文为转载文章,原文可读性会更好,文末有原文链接ps:出处代码基于androidapi27进行分析,本文我们分析Activity的setContentView方法到底做了什么?有些读者可能会有疑问。文章有些地方看不懂怎么办。不明白的地方可以先跳过,看完这篇文章以后再google不明白的地方;(2)不懂的地方先google一下,再继续看这篇文章。好了,言归正传,我们来看Activity的setContentView方法;publicvoidsetContentView(@LayoutResintlayoutResID){getWindow().setContentView(layoutResID);initWindowDecorActionBar();}这里的getWindow方法获取的是Window对象,这个Window是Display顶级窗口的外观,封装了findViewById和事件分发等一些基本行为,每个Window都会添加到WindowManager中。Window的唯一实现类是PhoneWindow,所以getWindow方法得到的是Window对象,本质上就是PhoneWindow对象;如何知道Window的唯一实现类是PhoneWindow?你可以从第一篇关于Android中View事件分发的文章中找到答案。再看PhoneWindow的setContentView方法;@OverridepublicvoidsetContentView(intlayoutResID){//注意:FEATURE_CONTENT_TRANSITIONS可能在安装窗口的过程中被设置//decor,当主题属性等被结晶时。在此之前不要检查功能//。if(mContentParent==null){//1、installDecor();}elseif(!hasFeature(FEATURE_CONTENT_TRANSITIONS)){mContentParent。删除所有视图();}if(hasFeature(FEATURE_CONTENT_TRANSITIONS)){鳍alScenenewScene=Scene.getSceneForLayout(mContentParent,layoutResID,getContext());过渡到(新场景);}else{//2,mLayoutInflater.inflate(layoutResID,mContentParent);}mContentParent.requestApplyInsets();最终Window.Callbackcb=getCallback();if(cb!=null&&!isDestroyed()){//3、cb.onContentChanged();}//4、mContentParentExplicitlySet=true;}注3表示回调Activity的onContentChanged方法;注4表示布局已设置。当mContentParentExplicitlySet=false时,PhoneWindow的requestFeature方法已经被调用。当mContentParentExplicitlySet=true时,不能再调用PhoneWindow的requestFeature方法。我们看看PhoneWindow的requestFeature方法;@OverridepublicbooleanrequestFeature(intfeatureId){if(mContentParentExplicitlySet){thrownewAndroidRuntimeException("在添加内容之前必须调用requestFeature()");}......}上面说到注1的意思是实例化DecorView并安装,我们来看看installDecor方法的实现主体实施;privatevoidinstallDecor(){mForceDecorInstall=false;if(mDecor==null){//5,mDecor=generateDecor(-1);......}else{mDecor.setWindow(this);}if(mContentParent==null){//6,mContentParent=generateLayout(mDecor);......}}注5中的mDecor是一个DecorView对象,也是我们Activity的最顶层视图,它的父类是FrameLayout,我们来看看PhoneWindow的generateDecor方法是如何创建一个DecorView对象的;protectedDecorViewgenerateDecor(intfeatureId){......returnnewDecorView(context,featureId,this,getAttributes());}直接新建一个DecorView并返回,我们回到注释6中的代码installDecor方法,即PhoneWindow的generateLayout方法;protectedViewGroupgenerateLayout(DecorViewdecor){//7、TypedArraya=getWindowStyle();…//8.mIsFloating=a.getBoolean(R.styleable.Window_windowIsFloating,false);intflagsToUpdate=(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)&(~getForcedWindowFlags());如果(mIsFloating){setLayout(WRAP_CONTENT,WRAP_CONTENT);setFlags(0,flagsToUpdate);}else{setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR,flagsToUpdate);}//9、if(a.getBoolean(R.styleable.Window_windowNoTitle,false)){requestFeature(FEATURE_NO_TITLE);}elseif(a.getBoolean(R.styleable.Window_windowActionBar,false)){//如果没有标题,则不允许使用操作栏。请求功能(FEATURE_ACTION_BAR);}......//10、mIsTranslucent=a.getBoolean(R.styleable.Window_windowIsTranslucent,false);......//11、intlayoutResource;//12、intfeatures=getLocalFeatures();//System.out.println("Features:0x"+Integer.toHexString(features));if((features&(1<看下注14的代码,就是DecorView的onResourcesLoaded方法;voidonResourcesLoaded(LayoutInflaterinflater,intlayoutResource){......mDecorCaptionView=createDecorCaptionView(inflater);最终视图root=inflater.inflater(layoutResource,null);//15,if(mDecorCaptionView!=null){if(mDecorCaptionView.getParent()==null){addView(mDecorCaptionView,newViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));}}mDecorCaptionView.addView(root,newViewGroup.MarginLayoutParams(MATCH_PARENT,MATCH_PARENT));//16,}else{//把它放在颜色视图下面。addView(root,0,newViewGroup.LayoutParams(MATCH_PARENT,MATCH_PARENT));}......}注释15中的代码表示,如果有标题,则判断mDecorCaptionView.getParent()是否为空,如果为空,则将mDecorCaptionView添加到DecorView中。如果不为空,则说明已经将mDecorCaptionView添加到DecorView中,最后直接将root添加到mDecorCaptionView中;注16表示没有标题,直接在DecorView中添加root如何判断Note15中蓝色描述的文字是否正确?我们可以看一下创建mDecorCaptionView的DecorView.createDecorCaptionView方法;privateDecorCaptionViewcreateDecorCaptionView(LayoutInflaterinflater){DecorCaptionViewdecorCaptionView=null;for(inti=getChildCount()-1;i>=0&&decorCaptionView==null;i--){查看视图=getChildAt(i);if(viewinstanceofDecorCaptionView){//装饰很可能是在重新启动时保存的-所以请重新使用它。//17、decorCaptionView=(DecorCaptionView)view;removeViewAt(我);}}。.....if(!mWindow.isFloating()&&isApplication&&StackId.hasWindowDecor(mStackId)){//根据所用标题的亮度,我们使用//深色或浅色按钮框架。if(decorCaptionView==null){//18,decorCaptionView=inflateDecorCaptionView(inflater);}装饰标题nView.setPhoneWindow(mWindow,true/*showDecor*/);}else{decorCaptionView=null;}......returndecorCaptionView;}注释17中,View被强制为DecorCaptionView,此时的View是DecorView的子View,证明上面提到的mDecorCaptionView已经加入到DecorView中;看注释18的代码,就是DecorView的inflateDecorCaptionView方法;privateDecorCaptionViewinflateDecorCaptionView(LayoutInflaterinflater){finalContextcontext=getContext();//我们制作了一个充气器的副本,因此它具有与之关联的正确上下文。inflater=inflater.from(上下文);最终DecorCaptionView视图=(DecorCaptionView)inflater.inflate(R.layout.decor_caption,null);setDecorCaptionShade(上下文,视图);returnview;}查看inflateDecorCaptionView方法中DecorCaptionView的创建,传入一个参数null,这里创建的DecorCaptionView对象的getParent()为空假设DecorView要加载的xml文件是screen_simple.xml,那么我们回头看DecorView的installDecor方法中注释6的代码,发现mContentParent就是id为screen_simple.xml中content的FrameLayout;回头看一下PhoneWindow的setContentView方法在Note2的代码中,发现我们的Activity设置的内容布局文件被添加到screen_simple.xml中id为content的FrameLayout中;将Activity设置的内容布局添加到DecorView中后,我就不画完整的布局结构了,大家可以看一下理解Android中的ViewRootImpl和DecorView的文末,会有图片。