Android中View可以說是最為重要的幾個地方之一,包括事件分發(fā)桶雀,測量,繪制等等唬复,都是非常常見的情況矗积。那么我們要想好好掌握這些知識,就得深入了解Andorid整個View從開始到完成所經(jīng)歷的一系列工作盅抚。本文分析的源代碼均來自Android API 24漠魏。
Activity和Window
在Android中,Activity并不負責(zé)視圖控制妄均,它只是控制生命周期和處理事件柱锹,真正控制視圖的是Window。一個Activity包含了一個Window丰包,Window才是真正代表一個窗口禁熏,也就是說Activity如果沒有Window,那就和Service沒多大差別了邑彪。
Window第一次出現(xiàn)在Activity中是在ActivityThread調(diào)用Activity的attach()方法時瞧毙,通過PolicyManager創(chuàng)建window,實現(xiàn)callback方法,所以,當window接收到
外界狀態(tài)改變時,會調(diào)用activity的方法:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
....
mWindow = new PhoneWindow(this, window); //在這里直接new一個Window
mWindow.setCallback(this);//設(shè)置回調(diào)接口,當window接收系統(tǒng)發(fā)送給它的例如鍵盤和觸摸屏事件,就可以通知Activity,Activity做出相應(yīng)的處理
.....
//設(shè)置窗口管理器
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
.....
}
在attach()中宙彪,Activity新建一個PhoneWindow實例作為成員變量,這是Window的唯一一個子類矩动。然后設(shè)置mWindow的WindowManager。
DecorView
首先簡單介紹一下DecorView释漆,這里引用任玉剛大神的《Android開發(fā)藝術(shù)探索》書中的介紹:它是一個頂級View悲没,內(nèi)部會包含一個豎直方向的LinearLayout,這個LinearLayout有上下兩部分男图,分為titlebar和contentParent兩個子元素示姿,contentParent的id是content,而我們自定義的Activity的布局就是contentParent里面的一個子元素逊笆。View層的所有事件都要先經(jīng)過DecorView后才傳遞給我們的View栈戳。那么DecorView是何時出現(xiàn)的呢?难裆?又是如何和window產(chǎn)生聯(lián)系呢子檀??
首先乃戈,如上圖我們可以看到Activity中包含了Window,DecorView,WindowManager等成員變量命锄,那么我們就從Activity的創(chuàng)建過程中去逐步尋找它們。
首先在Activity中的onCreate方法中我們都會寫一句setContentView的方法調(diào)用偏化,將我們自定義的Activity布局傳入。那么我們跟進該方法去看一下具體實現(xiàn)镐侯。
通過getWindow來獲取成員變量mWindow侦讨。
然后調(diào)用window的setContentView()方法。該方法來進行我們Activity的布局設(shè)置苟翻。然后又接著調(diào)用了initWindowDecorActionBar()方法來進行對ActionBar的新建和初始化韵卤。我們暫時不管該方法。
mWindow類型是Window崇猫,是一個接口沈条,而在安卓中實現(xiàn)Window接口的實現(xiàn)類只有PhoneWindow,所以進入PhoneWindow查看setContentView()方法的具體實現(xiàn)诅炉。先上源代碼:
首先會在上圖標記1處判斷mContentParent是否為空蜡歹,如果為空,會執(zhí)行installDecor()方法涕烧,那么mContentParent是什么呢月而?
繼續(xù)回到setContentView中看代碼,如果mContentParent為空世杀,那么會執(zhí)行installDecor()方法,跟進這個方法,顧名思義肝集,新建DecorView瞻坝。下面給出源代碼:
在installDecor方法中標注1處,如果mDecor為空(mDecor是Window持有的一個成員變量包晰,指的就是DecorView)湿镀,那么在 generateDecor()中去實例化新建DecorView,我們繼續(xù)跟進generateDecor()查看源代碼:
在這里前面一段代碼都是初始化所需要的context,最后返回一個實例化的DecorView()伐憾。在DecorView構(gòu)造方法中進行初始化工作勉痴。
上圖是DecorView的一些初始化工作,就不再展開树肃,有興趣的同志們可以研究蒸矛。
返回到installDecor()方法中繼續(xù)往下,到標記處2胸嘴,會判斷mContentParent是否為空雏掠,如果為空,通過 generateLayout()來將我們具體的布局加載到DecorView中劣像。跟入 generateLayout()方法查看源代碼(由于方法中代碼過長不便于截圖乡话,我貼出關(guān)鍵代碼):
...//代碼前面都是在獲取主題相關(guān)
//在這個方法里將mContentParent的布局轉(zhuǎn)換并添加到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);//具體代碼在下一個代碼框中
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);// contentParent就是root的父布局。
return contentParent;
final View root = inflater.inflate(layoutResource, null);//將mContentParent布局轉(zhuǎn)換為View
mDecor.addView(root,new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));//將mContentParent加入到DecorView中
mContentRoot = (ViewGroup) root;//mContentRoot就是mContentParent視圖
至此generateLayout()方法分析完畢耳奕,返回contentParent變量绑青,賦值給mContentParent成員變量。
圖中標注處2的代碼闸婴,則是將我們的自定義布局加入到mContentParent(也就是id為R.id.content)布局中。
最后標記處3則是通過回調(diào)來通知ActivityContent已經(jīng)發(fā)生了變化芍躏,由Activity來做出相應(yīng)的處理邪乍。
DecorView和Window
至此已經(jīng)setContentView已經(jīng)完成,DecorView也已經(jīng)新建对竣,我們自定義布局也加入到mContentParent布局中庇楞,但是此時mDecorView還沒有被WindowManager正式添加到Window中。因此Window此時還無法接受外界的信息柏肪,也無法提供具體的功能姐刁。
所以在activity中的onResume()方法中調(diào)用了makeVisible()方法來進行添加,同時也是把Window加入到WindowManager中
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager(); //獲得WindowManager(WindowManager extends ViewManager)
wm.addView(mDecor, getWindow().getAttributes()); //將DecorView加入到Window中
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
至此整個decorView的加載過程和Activity的Window創(chuàng)建過程已經(jīng)完成烦味,Decor將會顯示出來呈現(xiàn)在手機屏幕上.
ps:由于是大二萌新第一次寫東西聂使,所以很多地方還很模糊壁拉,希望前輩們多多指點錯誤,也希望能繼續(xù)加油記錄自己的點點滴滴
再ps:本文參考和學(xué)習(xí)了任玉剛大神的《Android開發(fā)藝術(shù)探索》和陳育大神的文章(http://www.reibang.com/p/687010ccad66)