東坡下載:內(nèi)容最豐富最安全的下載站!

幫助|文件類型庫(kù)|最新更新|下載分類|排行榜

首頁(yè)安卓軟件其他軟件 → OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app) 6.1安卓版
OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)6.1安卓版

OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)

更新:2017-02-13 10:18

大。3.3M

下載地址
更多安卓版 >
更多IOS版 >
更多PC版 >
二維碼

掃描二維碼安裝到手機(jī)

好玩50%(10票)
坑爹50%(10票)

同一開發(fā)者

OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)截圖OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)截圖OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)截圖OpenStax CNX(為安卓系統(tǒng)建立聯(lián)系app)截圖
  • 分類:其他軟件
  • 大。3.3M
  • 語(yǔ)言:中文
  • 版本:6.1安卓版
  • 時(shí)間:2017-02-13 10:18
  • 星級(jí):
  • 官網(wǎng):暫無(wú)
  • 廠商
  • 平臺(tái):Android

為安卓系統(tǒng)建立聯(lián)系是自由教育資源全世界貯藏庫(kù)被出版根據(jù)一個(gè)創(chuàng)造性的共同性歸屬執(zhí)照。保存項(xiàng)目對(duì)您的喜愛并且觀看連接使所有滿意在機(jī)器人的連接App。

為安卓系統(tǒng)建立聯(lián)系功能特色

1.在應(yīng)用程序視圖的教科書。

2.在圖書搜索

3.返回你在本書前面的位置

4.搜索OpenStaxCNX您需要的內(nèi)容。

5.記筆記的應(yīng)用程序,它們通過(guò)電子郵件或文本導(dǎo)出為文本文件到您的手機(jī)或共享。

Android-如何把Layout和Activity建立起聯(lián)系

一、問題

1.Xml文件是布局基礎(chǔ),但是它是怎么樣和Activity建立聯(lián)系的,作為視圖展示到手機(jī)屏幕上的?

2.findViewById()是怎樣找到對(duì)應(yīng)的Xml文件中的元素并把Xml文件中的元素展示成一個(gè)View?

3.怎樣把一個(gè)Xml文件解析成View展示出來(lái)的呢?

二、幾個(gè)關(guān)鍵的對(duì)象

DecorViewmDecor;//Thisisthetop-levelviewofthewindow,containingthewindowdecor.

ViewGroupmContentParent;//Thisistheviewinwhichthewindowcontentsareplaced.ItiseithermDecoritself,orachildofmDecorwherethecontentsGo.

ViewGroupmContentRoot;//Thisistheviewinwhichthewindowcontents

LayoutInflatermLayoutInflater;//

三、Window展示視圖的結(jié)構(gòu)

在手機(jī)上展示出來(lái)的內(nèi)容結(jié)構(gòu)是下圖中這樣的,在最外層有一個(gè)頂級(jí)容器DecorView,然后是我們的內(nèi)容的根視圖mContentRoot(ViewGroup),然后才是我們Xml或者new出來(lái)的View。

四、從源碼了解

從setContentView(layoutResID)著手,一般我們?cè)O(shè)置Activity的Layout時(shí)都是通過(guò)該方法設(shè)置對(duì)應(yīng)的layoutId,然后把layoutId對(duì)應(yīng)的Xml文件解析成我們看到的視圖界面,所以入手點(diǎn)就是我們熟知并且使用過(guò)千百遍的setContentView(layoutResID),先看一下源碼:

publicvoidsetContentView(intlayoutResID){

//Note:FEATURE_CONTENT_TRANSITIONSmaybesetintheprocessofinstallingthewindow

//decor,whenthemeattributesandthelikearecrystalized.Donotcheckthefeature

//beforethishappens.

if(mContentParent==null){//step1

installDecor();

}elseif(!hasFeature(FEATURE_CONTENT_TRANSITIONS)){

mContentParent.removeAllViews();

}

if(hasFeature(FEATURE_CONTENT_TRANSITIONS)){

finalScenenewScene=Scene.getSceneForLayout(mContentParent,layoutResID,

getContext());

transitionTo(newScene);

}else{

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

}

……

}

這段代碼就是本文的入口,從step1開始分析,先判斷mContentParent是否是空的,如果是空的則執(zhí)行installDecor(),初始化后第一次打開頁(yè)面mContentParent肯定是空的,所以執(zhí)行installDecor()方法,先不用管對(duì)應(yīng)的elseif判斷條件中的內(nèi)容,這不是我們要了解的重點(diǎn),那么接下來(lái)看一下installDecor()是干什么的呢:

privatevoidinstallDecor(){

if(mDecor==null){

mDecor=generateDecor();

……

}

if(mContentParent==null){

mContentParent=generateLayout(mDecor);

……

}

}

這個(gè)方法的比較長(zhǎng),大部分是和本文的主題不相關(guān)的,關(guān)鍵的也就那么幾行,去掉不重要的代碼讓我們的思路更清晰。只要找準(zhǔn)這幾個(gè)關(guān)鍵的地方就可以明白這個(gè)所表達(dá)的真正含義了,其它的都是附屬品。從方法名的字面意思可以看出這個(gè)方法的目的就是install展示內(nèi)容的Decor(DecorView),這就是我們?cè)谀夸浂刑岬降年P(guān)鍵對(duì)象之一,這個(gè)對(duì)象是做什么的呢,它就是手機(jī)上看到的應(yīng)用視圖的頂級(jí)View,所有的在手機(jī)上呈現(xiàn)出來(lái)的view的頂級(jí)容器,它繼承自FrameLayout,每一個(gè)打開的手機(jī)窗口首先都是有一個(gè)頂級(jí)的容器來(lái)裝載我們要展示的內(nèi)容。從第一個(gè)if語(yǔ)句開始,如果mDecor是null則mDecor=generateDecor(),generateDecor()的目的是生成一個(gè)沒有feature的DecorView。再看第二個(gè)if語(yǔ)句,它的目的是生成mContentParent,也是目錄二中提到的關(guān)鍵對(duì)象之一(它是這是窗口內(nèi)容被放置的視圖,它可以是mDecor本身,也可以是一個(gè)子mDecor的內(nèi)容,這里就要視情況而論了,當(dāng)作為子view(inflate一個(gè)view的時(shí)候)就是mDecor本身),這個(gè)方法的內(nèi)容也是非常多,關(guān)鍵的內(nèi)容也還是那么幾行,其它的都是針對(duì)設(shè)置的feature做相應(yīng)的配置信息,例如,actionbar、floatWindow等。

/**

*TheIDthatthemainlayoutintheXMLlayoutfileshouldhave.

*/

publicstaticfinalintID_ANDROID_CONTENT=com.android.internal.R.id.content;

protectedViewGroupgenerateLayout(DecorViewdecor){

……

mDecor.startChanging();

……

ViewGroupcontentParent=(ViewGroup)findViewById(ID_ANDROID_CONTENT);

//ID_ANDROID_CONTENT是com.android.internal.R.id.content,這就是內(nèi)容展示的主要view

……

returncontentParent;

}

這樣1千多行的代碼就被很好的分解了,得到希望看到的內(nèi)容,剔除掉和目的不相關(guān)的干擾項(xiàng)剩下的就是真相。這個(gè)過(guò)程大體就可以清楚了,通過(guò)findViewById找到google定義的一個(gè)內(nèi)部view,賦給contentParent作為返回內(nèi)容,然后再回到setContentView(layoutResID)中看關(guān)鍵代碼:

if(hasFeature(FEATURE_CONTENT_TRANSITIONS)){

finalScenenewScene=Scene.getSceneForLayout(mContentParent,layoutResID,

getContext());

transitionTo(newScene);

}else{

mLayoutInflater.inflate(layoutResID,mContentParent);//這是重點(diǎn)

}

繞了半天終于用到了我們最關(guān)心的一個(gè)變量layoutResID,這一句代碼是不是很熟悉,在使用listview的時(shí)候,getView中常會(huì)用到的或者使用fragment時(shí)常用到的,當(dāng)然還有很多地方我們都會(huì)用到,例如,here。

接下來(lái),查看方法inflate:

publicViewinflate(@LayoutResintresource,@NullableViewGrouproot){

returninflate(resource,root,root!=null);

}

然后再進(jìn)入到方法inflate(resource,root,root!=null):

publicViewinflate(@LayoutResintresource,@NullableViewGrouproot,booleanattachToRoot){

finalResourcesres=getContext().getResources();

……

finalXmlResourceParserparser=res.getLayout(resource);

try{

returninflate(parser,root,attachToRoot);

}finally{

parser.close();

}

}

現(xiàn)在離我們的目的已經(jīng)不遠(yuǎn)了,其實(shí)已經(jīng)很明了了,就是通過(guò)一個(gè)Xml解析器解析我們的Xml文件,然后返回解析后的View,我們繼續(xù)往下看:

publicViewinflate(XmlPullParserparser,@NullableViewGrouproot,booleanattachToRoot){

synchronized(mConstructorArgs){

Trace.traceBegin(Trace.TRACE_TAG_VIEW,"inflate");//記錄解析日志

finalContextinflaterContext=mContext;

finalAttributeSetattrs=Xml.asAttributeSet(parser);//通過(guò)parser中得到layout中的所有view的屬性集保存在attrs中

ContextlastContext=(Context)mConstructorArgs[0];

mConstructorArgs[0]=inflaterContext;

Viewresult=root;

try{

//Lookfortherootnode.

……

finalStringname=parser.getName();//得到layout的節(jié)點(diǎn)name,例如,view、merge、include等

……

if(TAG_MERGE.equals(name)){//這里忽略,先不研究merge

if(root==null||!attachToRoot){

thrownewInflateException("<merge/>canbeusedonlywithavalid"

+"ViewGrouprootandattachToRoot=true");

}

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

}else{//忽略merge后的入口entrence

//Tempistherootviewthatwasfoundinthexml

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

ViewGroup.LayoutParamsparams=null;

if(root!=null){

//Createlayoutparamsthatmatchroot,ifsupplied

params=root.generateLayoutParams(attrs);

if(!attachToRoot){//note1

//Setthelayoutparamsfortempifwearenot

//attaching.(Ifweare,weuseaddView,below)

temp.setLayoutParams(params);

}

}

//Inflateallchildrenundertempagainstitscontext.

rInflateChildren(parser,temp,attrs,true);

//Wearesupposedtoattachalltheviewswefound(inttemp)

//toroot.Dothatnow.

if(root!=null&&attachToRoot){

root.addView(temp,params);

}

//Decidewhethertoreturntherootthatwaspassedinorthe

//topviewfoundinxml.

if(root==null||!attachToRoot){

result=temp;

}

}

}catch(XmlPullParserExceptione){

……

}catch(Exceptione){

……

}finally{

……

}

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

returnresult;

}

}

在這個(gè)方法中,通過(guò)resource在parser解析出layout中所有元素的屬性然后放在變量attrs中,然后在上述代碼紅色標(biāo)記的entrence處調(diào)用createViewFromTag方法根據(jù)attrs屬性集中的屬性創(chuàng)建出對(duì)應(yīng)的view,到這兒,基本上已經(jīng)可以大概知道view創(chuàng)建的流程,為了更詳細(xì)的去了解過(guò)程,我們有必要看剩下最后一個(gè)關(guān)鍵的方法createViewFromTag(root,name,inflaterContext,attrs):

ViewcreateViewFromTag(Viewparent,Stringname,Contextcontext,AttributeSetattrs,

booleanignoreThemeAttr){

……

Viewview;

if(mFactory2!=null){

view=mFactory2.onCreateView(parent,name,context,attrs);

}elseif(mFactory!=null){

view=mFactory.onCreateView(name,context,attrs);

}else{

view=null;

}

if(view==null&&mPrivateFactory!=null){

view=mPrivateFactory.onCreateView(parent,name,context,attrs);

}

if(view==null){

finalObjectlastContext=mConstructorArgs[0];

mConstructorArgs[0]=context;

try{

if(-1==name.indexOf('.')){

view=onCreateView(parent,name,attrs);

}else{

view=createView(name,null,attrs);

}

}finally{

mConstructorArgs[0]=lastContext;

}

}

returnview;

……

}

這里就是把從Xml中解析出來(lái)的內(nèi)容根據(jù)變量name生成對(duì)應(yīng)的View對(duì)象,其實(shí)后面的實(shí)現(xiàn)不用看源碼也可以想到了,用反射生成對(duì)應(yīng)的View對(duì)象,然后一級(jí)一級(jí)的向來(lái)時(shí)的路返回給調(diào)用方法。但是為了證實(shí)我們的猜測(cè)還是要仔細(xì)的研究一番,先看第一個(gè)if語(yǔ)句,很簡(jiǎn)單,就是通過(guò)工廠去創(chuàng)建View,Activity實(shí)現(xiàn)了接口Factory2,在Activity源碼中可以看到具體實(shí)現(xiàn),進(jìn)入Activity查看源碼:

Factory2:

publicViewonCreateView(Viewparent,Stringname,Contextcontext,AttributeSetattrs){

if(!"fragment".equals(name)){

returnonCreateView(name,context,attrs);

}

returnmFragments.onCreateView(parent,name,context,attrs);

}

Factory:

publicViewonCreateView(Stringname,Contextcontext,AttributeSetattrs){

returnnull;

}

可以看到如果我們沒有使用fragment,則最后返回的都是null,那么再回到createViewFromTag方法中繼續(xù)看下面的代碼,mPrivateFactory也是Factory2的一個(gè)對(duì)象,所以還是一樣的看Activity中代碼,得出同樣的結(jié)果返回null,這樣的話,真正創(chuàng)建View的代碼就是通過(guò)第三個(gè)if語(yǔ)句實(shí)現(xiàn)的,找到關(guān)鍵地方try包裹的代碼,view=onCreateView(parent,name,attrs)和view=createView(name,null,attrs)兩個(gè)方法最終實(shí)現(xiàn)都會(huì)調(diào)用createView(Stringname,Stringprefix,AttributeSetattrs),這樣我們就可以找到源頭了:

publicfinalViewcreateView(Stringname,Stringprefix,AttributeSetattrs)

throwsClassNotFoundException,InflateException{

Constructor<?extendsView>constructor=sConstructorMap.get(name);

Class<?extendsView>clazz=null;

try{

Trace.traceBegin(Trace.TRACE_TAG_VIEW,name);

if(constructor==null){

//Classnotfoundinthecache,seeifit'sreal,andtrytoaddit

clazz=mContext.getClassLoader().loadClass(

prefix!=null?(prefix+name):name).asSubclass(View.class);

if(mFilter!=null&&clazz!=null){

booleanallowed=mFilter.onLoadClass(clazz);

if(!allowed){

failNotAllowed(name,prefix,attrs);

}

}

constructor=clazz.getConstructor(mConstructorSignature);

constructor.setAccessible(true);

//這里是view緩存

sConstructorMap.put(name,constructor);

}else{

//Ifwehaveafilter,applyittocachedconstructor

if(mFilter!=null){

//Haveweseenthisnamebefore?

BooleanallowedState=mFilterMap.get(name);

if(allowedState==null){

//Newclass--rememberwhetheritisallowed

clazz=mContext.getClassLoader().loadClass(

prefix!=null?(prefix+name):name).asSubclass(View.class);

booleanallowed=clazz!=null&&mFilter.onLoadClass(clazz);

mFilterMap.put(name,allowed);

if(!allowed){

failNotAllowed(name,prefix,attrs);

}

}elseif(allowedState.equals(Boolean.FALSE)){

failNotAllowed(name,prefix,attrs);

}

}

}

Object[]args=mConstructorArgs;

args[1]=attrs;

finalViewview=constructor.newInstance(args);

if(viewinstanceofViewStub){

//UsethesamecontextwheninflatingViewStublater.

finalViewStubviewStub=(ViewStub)view;

viewStub.setLayoutInflater(cloneInContext((Context)args[0]));

}

returnview;

……

}

這樣就證實(shí)我們的猜想,確實(shí)是通過(guò)反射來(lái)創(chuàng)建View,然后我們的任務(wù)也就完成了。需要注意的是,通過(guò)反射創(chuàng)建的View對(duì)象返回的都是View類型的對(duì)象,在使用時(shí)需要強(qiáng)制轉(zhuǎn)換?梢钥偨Y(jié)為:在activity中指定的layoutId去找到對(duì)應(yīng)的Xml文件,然后通過(guò)Xml解析生成對(duì)應(yīng)的View然后inflate到窗口頂級(jí)容器DecorView中繪制展現(xiàn)出來(lái)。

回到我們的目錄二問題中,1和3都已經(jīng)清楚了,那么問題2是什么樣的結(jié)果呢,其實(shí)這個(gè)很簡(jiǎn)單,在創(chuàng)建View的時(shí)候已經(jīng)從Xml文件中解析到完整的view屬性attrs,在使用反射創(chuàng)建view時(shí)會(huì)通過(guò)構(gòu)造函數(shù)生成對(duì)應(yīng)的對(duì)象,所以會(huì)用到View的構(gòu)造方法,在View(Contextcontext,@NullableAttributeSetattrs,intdefStyleAttr,intdefStyleRes)方法中有一句代碼mID=a.getResourceId(attr,NO_ID),這樣就可以從attribute中把解析到的ID放在mID變量中,然后在findViewByID(id)中,根據(jù)參數(shù)id返回對(duì)應(yīng)的View,需要注意的是,在findViewById時(shí)要用到findViewTraversal(@IdResintid)方法,在這里如果指定find的范圍(比如,在FrameLayout中去找),則使用ViewGroup中的findViewTraversal(@IdResintid)方法,先獲取到ViewGroup的所有子View,然后通過(guò)遍歷子View找到對(duì)應(yīng)的View并返回,查看下面代碼。

protectedViewfindViewTraversal(@IdResintid){

if(id==mID){//如果等于當(dāng)前ViewGroup的Id,則返回該ViewGroup

returnthis;

}

finalView[]where=mChildren;

finalintlen=mChildrenCount;

for(inti=0;i<len;i++){//其它情況則遍歷所有子View,返回對(duì)應(yīng)的View

Viewv=where[i];

if((v.mPrivateFlags&PFLAG_IS_ROOT_NAMESPACE)==0){

v=v.findViewById(id);

if(v!=null){

returnv;

}

}

}

returnnull;

}

note1:記得在使用listView的時(shí)候的最后一個(gè)boolean參數(shù)時(shí),看到很多人都有使用該變量,也沒去詳細(xì)了解,只是習(xí)慣性的去使用。在note1標(biāo)記處給出了合理的解釋,大概大概意思是如果我們沒有為temp設(shè)置params則使用setLayoutParams(temp.setLayoutParams(params))方法設(shè)置,如果設(shè)置過(guò)則使用addView方法為temp添加params(root.addView(temp,params))。

更新內(nèi)容

1,優(yōu)化了操作

2,修復(fù)了已知bug

應(yīng)用信息

  • 包名:org.cnx.android
  • 名稱:OpenStax CNX
  • 版本:6.1
  • 版本號(hào):36
  • MD5:df53a0e6b8c064cd592ca817f7d18c28

網(wǎng)友評(píng)論

熱門評(píng)論
最新評(píng)論
發(fā)表評(píng)論 查看所有評(píng)論(0)
昵稱:
表情: 高興 可 汗 我不要 害羞 好 下下下 送花 屎 親親
字?jǐn)?shù): 0/500 (您的評(píng)論需要經(jīng)過(guò)審核才能顯示)

推薦軟件

下載排行