因?yàn)閍ndroid各版本的布局層級(jí)會(huì)有所差異摔癣,所以先告訴大家我測(cè)試的環(huán)境背景跛璧,如有在別的系統(tǒng)版本下面測(cè)試的結(jié)果有所出入請(qǐng)?jiān)谙旅媪粞灾С觯奖愀嗟淖x者可以從中獲益歇拆,謝謝大家迎变!
android studio:2.2.2
java版本:1.8
系統(tǒng)版本:ubuntu
sdk版本:minSdkVersion 19汰现,?targetSdkVersion 25
手機(jī)版本:樂視6.01
前言
當(dāng)我們新建一個(gè)應(yīng)用的時(shí)候如果選擇的是創(chuàng)建一個(gè)空的activity啼染,那么AS默認(rèn)會(huì)給我們重寫onCreate()方法,并且在這個(gè)方法中為我們添加上setContenView方法湖蜕,我們常規(guī)的做法是從這里進(jìn)入對(duì)應(yīng)的布局文件中刪掉不必要的代碼然后開始我們的布局代碼編寫。今天我們的講解就從注釋掉setContentView方法開始深入的講解一下activity中布局的層級(jí)問題宋列。
在這之前我先教大家如何使用AS查看應(yīng)用的布局昭抒,當(dāng)手機(jī)鏈接上AS之后,我們?cè)诖蛴ogcat的地方也就是Android monitor標(biāo)簽這里會(huì)看到這樣一個(gè)一行選項(xiàng):
箭頭所示的圖標(biāo)就是我們用來分析app布局的利器:layout inspector炼杖,今天我們的布局層級(jí)也是通過它來分析的灭返。
一、不調(diào)用setContentView的情況下的布局層級(jí)
首先我們新見一個(gè)帶有一個(gè)空白activity的app坤邪,取名LayoutHierarchy熙含,下面的文章會(huì)簡(jiǎn)稱為L(zhǎng)H,建好應(yīng)用之后我們注釋掉setContentView這句代碼,然后運(yùn)行到手機(jī)上面艇纺,然后點(diǎn)擊上面說的layoutInspector工具怎静,過幾秒之后AS會(huì)打開類似下面這樣的界面:
這里我標(biāo)出來了整個(gè)窗口大致分為這幾個(gè)部分,今天主要用到的上面的4個(gè)并排中的后面三個(gè)黔衡,他們分別是:用來查看層級(jí)的窗口蚓聘,用來查看運(yùn)行效果的窗口,用來詳細(xì)顯示選中層級(jí)中的某個(gè)布局的詳細(xì)參數(shù)狀況盟劫。
將上面的3個(gè)窗口編號(hào)分別為:1夜牡、2、3侣签,我們首先來看下窗口一中有那些東西:
別看到張開后這么復(fù)雜塘装,其實(shí)我們折疊好之后之后PhoneWindow$DecorView這一個(gè),然后我們?cè)俅握归_一層影所,我們發(fā)現(xiàn)DecorView有2個(gè)子布局分別是LinearLayout和PhoneWindow$ImmersiveView蹦肴,他們分別是我們的activity的根布局和狀態(tài)欄的布局。繼續(xù)往下展開猴娩,我們發(fā)現(xiàn)LinearLayout也有2個(gè)子布局阴幌,分別是ViewStub和FrameLayout,其中前面一個(gè)和actionbar有關(guān)胀溺,后面一個(gè)和我們的布局有關(guān)裂七。再次展開FrameLayout,我們發(fā)現(xiàn)其只有一個(gè)子布局:ActionBarOverlayLayout仓坞,因?yàn)槲覀冞@個(gè)activity含有actionbar所以系統(tǒng)幫我們多套用了這層布局背零。接著展開,我們發(fā)現(xiàn)這個(gè)布局有2個(gè)子布局无埃,分別為:ContentLayout和ActionBarContainer徙瓶,前面一個(gè)是和我們SetContentView密切相關(guān)的一層布局毛雇,我們的SetContentView里面的布局就是添加在這層布局里面的,因?yàn)槲覀儧]有調(diào)用setContentView方法侦镇,所以這里沒有子布局灵疮,后面的ActionBarContainer顧名思義就是和ActionBar相關(guān)的。我們展開ActionBarContainer發(fā)現(xiàn)其也有2個(gè)子布局:ToolBar和ActionBarContextView壳繁,因?yàn)槲覀兊腁ctivity不是直接繼承的Activity而是繼承了AppCompatActivity震捣,所以這里的ActionBar其實(shí)是ToolBar,這也就是我們?yōu)槭裁词褂眉嫒莸念惖臅r(shí)候在Activity中獲取ActionBar不是直接調(diào)用的getActionBar()方法而是調(diào)用的getSupportActionBar()方法闹炉,之后我們教大家一種全中文圈沒幾個(gè)人使用的方式來獲取我們的actionbar蒿赢,這里的兩個(gè)子布局ToolBar和ActionBarContextView,前面一個(gè)自然是我們的ActionBar渣触,后面的一個(gè)呢就是在使用actionbar的風(fēng)格為splite分離模式的時(shí)候使用的羡棵。刨根問底,最后我們?cè)俅握归_ToolBar嗅钻,我們發(fā)現(xiàn)其也是2個(gè)子布局:AppCompatTextView 和ActionMenuView皂冰,前面一個(gè)呢就是用來顯示我們的標(biāo)題的也就是大家看到的那個(gè)顯示我們應(yīng)用app的LayoutHierarchy的那個(gè)地方,后面的一個(gè)就是用來顯示actionbar的別的圖標(biāo)的养篓,比如home的返回按鈕等秃流。
到這里呢我就給大家逐層的分析了一遍布局的層級(jí),大家是不是很驚訝觉至,我們這是新建了一個(gè)activity沒有添加我們自己的布局的情況下已經(jīng)有這么多層的布局了剔应,要是加上我們自己的布局睡腿,那一個(gè)頁(yè)面是需要渲染多少次才可以繪制出來坝镉!而為了性能優(yōu)化席怪,我們必須要減少布局的層級(jí)应闯,這樣我們的app才能更快的渲染出來,才不會(huì)出現(xiàn)卡頓的現(xiàn)象挂捻。
另類的方式獲取actionBar
上面我們分析的時(shí)候知道其實(shí)在這里actionbar使用的是toolbar碉纺,除了使用activity的getSupportACtionbar的方式獲取到之外,我們可以直接使用FindviewById方法來獲取actionbar刻撒,這里就不得不使用我們前面3個(gè)窗口中的第三個(gè)窗口了骨田。在第一個(gè)窗口中我們選中ToolBar,然后第三個(gè)窗口會(huì)顯示類似下面的這種情況:
這里列出的各個(gè)屬性都是關(guān)于toolbar的声怔,下面還有一些沒有截取出來态贤,大家如果有興趣可以逐個(gè)的百度一下看看他們分別是用來控制toolbar的什么效果的,看見我在圖中標(biāo)出來的mId沒有醋火,沒錯(cuò)悠汽,這個(gè)就是toolbar在系統(tǒng)的id值箱吕。
來到代碼中,我們使用findviewbyId的方式來獲取到toolbar并且將他設(shè)置成紅色柿冲,修改后的代碼如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//? ? ? ? setContentView(R.layout.activity_main);
Toolbar toolbar = ((Toolbar) findViewById(R.id.action_bar));
toolbar.setBackgroundColor(Color.RED);
}
}
運(yùn)行后的效果如下:
我們看到整個(gè)狀態(tài)欄和標(biāo)題欄都變成紅色的了茬高,如果只是需要標(biāo)題欄是紅色的,狀態(tài)欄不變色該怎么辦呢假抄?如果我們是21以上的手機(jī)怎栽,只需要設(shè)置調(diào)用方法getWindow().setStatusBarColor()方法就可以,添加后的代碼如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//? ? ? ? setContentView(R.layout.activity_main);
Toolbar toolbar = ((Toolbar) findViewById(R.id.action_bar));
toolbar.setBackgroundColor(Color.RED);
getWindow().setStatusBarColor(Color.BLACK);
}
}
好了宿饱,后了上面的代碼基礎(chǔ)之后婚瓜,我們?cè)O(shè)置狀態(tài)欄和actionbar的顯示與隱藏,顏色值樣式值就變得非常的容易了刑棵,這里就不帶大家一般般的去實(shí)現(xiàn)了巴刻,有不懂的歡迎在下面留言。
因?yàn)榫W(wǎng)上流氓的網(wǎng)站太多了蛉签,經(jīng)常有網(wǎng)站不經(jīng)過我同樣就轉(zhuǎn)載我的文章胡陪,這里貼上我的博客地址,好讓大家看見文章的時(shí)候知道作者是誰(shuí)碍舍,我的博客地址:blog.csdn.net/qq379454816.
ok柠座,下面就來教大家如何通過不設(shè)置setContentView就可以顯示我們的布局,這里我使用上面帶大家分析布局層級(jí)的時(shí)候告訴大家的一個(gè)方法片橡,就是上面說的那個(gè)FrameLayout妈经,我們使用findviewById找到它,然后添加上我們的布局文件捧书,我們的布局文件就是一個(gè)款和高都是marchParent的一個(gè)imageView吹泡,修改后代碼如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//? ? ? ? setContentView(R.layout.activity_main);
Toolbar toolbar = ((Toolbar) findViewById(R.id.action_bar));
toolbar.setBackgroundColor(Color.RED);
getWindow().setStatusBarColor(Color.BLACK);
FrameLayout framelayout = (FrameLayout) findViewById(android.R.id.content);
View view = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
framelayout.addView(view, 0);
}
}
運(yùn)行上面的代碼就可以成功的加載上我們的布局文件,效果圖如下:
看到這里你有沒有一點(diǎn)小激動(dòng)呢经瓷?是不是對(duì)以前的很多細(xì)節(jié)都恍然大悟呢爆哑?原來google給我們?cè)O(shè)計(jì)android系統(tǒng)的時(shí)候考慮到安全因素之外還是給我們提供了很多的途徑去實(shí)現(xiàn)新東西的,只是我們?nèi)鄙侔l(fā)現(xiàn)的精神舆吮,網(wǎng)上的文章也是千遍一律揭朝,我們百度只是了解那些東西而不是用來照搬的,所以我們要多去實(shí)踐色冀。
上面使用findviewbyId()方法也可以用在activity里面是fragment的情況潭袱,我們直接可以把fragment放置到這個(gè)id為content的里面,這樣可以減少一層布局锋恬。
全屏去掉actionbar的時(shí)候情況
下面我們來看看全屏沒有actionbar的情況下的布局情況屯换,我們?cè)趕tyle文件中將主題設(shè)置為:android:style/Theme.Holo.NoActionBar,然后設(shè)置屬性:?true伶氢,在activity中將前面獲取toolbar的代碼注釋掉趟径,如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//? ? ? ? setContentView(R.layout.activity_main);
//? ? ? ? Toolbar toolbar = ((Toolbar) findViewById(R.id.action_bar));
//? ? ? ? toolbar.setBackgroundColor(Color.RED);
//? ? ? ? getWindow().setStatusBarColor(Color.BLACK);
//
FrameLayout framelayout = (FrameLayout) findViewById(android.R.id.content);
View view = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
framelayout.addView(view, 0);
}
運(yùn)行后我們看下效果:
屏幕上面就一張圖片瘪吏,我們打開layoutInspector來看看這個(gè)情況下的布局:
可以看到就算是這樣的情況也會(huì)有3層布局,這里我們先不討論google為什么這樣設(shè)計(jì)蜗巧,文章最后總結(jié)的時(shí)候我會(huì)告訴大家為什么要這樣設(shè)計(jì)掌眠。
二、使用setcontentView
我們先將代碼還原到創(chuàng)建app的初始狀態(tài)幕屹,如下所示:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
////? ? ? ? Toolbar toolbar = ((Toolbar) findViewById(R.id.action_bar));
////? ? ? ? toolbar.setBackgroundColor(Color.RED);
////? ? ? ? getWindow().setStatusBarColor(Color.BLACK);
////
//? ? ? ? FrameLayout framelayout = (FrameLayout) findViewById(android.R.id.content);
//? ? ? ? View view = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
//? ? ? ? framelayout.addView(view, 0);
}
}
看下運(yùn)行效果:
LayoutInspector中的情況:
這種情況和我們使用findview替換的方式其實(shí)是一樣多的布局蓝丙,為什么會(huì)這樣呢?我們查看源碼發(fā)現(xiàn)望拖,其實(shí)底層的setcontentView 的代碼也是find到content替換的:
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
總結(jié)
今天給大家介紹這篇文章的目的是要大家認(rèn)識(shí)到布局是怎樣一層一層的繪制到屏幕上面的渺尘,只有我們清楚它的繪制流程我們才可以在使用的過程中更加靈活的應(yīng)對(duì)出現(xiàn)問題的時(shí)候也可以做到更加的從容。如果在使用fragment的情況下我們就可以直接用content來作為fragment的容器而不需要再次設(shè)置一個(gè)容器说敏,當(dāng)然這只能適合activity只有一個(gè)fragment的情況鸥跟。那么google為什么在一個(gè)簡(jiǎn)單的activity上面設(shè)置那么多層次的布局呢?豈不是很浪費(fèi)性能盔沫?其實(shí)google這樣設(shè)計(jì)也是有原因的医咨,我們的視圖不只是用來展現(xiàn)一個(gè)畫面給用戶,我們更過的是讓手機(jī)與用戶去交互架诞,交互的話就避免不了處理觸摸和點(diǎn)擊事件拟淮,那么如何一層層的給點(diǎn)擊事件處理好呢?你當(dāng)然想到了是否分發(fā)和攔截的情況谴忧,沒錯(cuò)很泊,google這樣設(shè)計(jì)就是為了在系統(tǒng)層可以更好的控制點(diǎn)擊事件的分發(fā)。那么這樣就可以只添加一個(gè)LinearLayout就可以了沾谓,為什么下面還添加一個(gè)FrameLayout委造?也許他這樣設(shè)計(jì)還處于考慮別的原因,我暫時(shí)不知道為什么這樣就設(shè)計(jì)搏屑,如果有知道的同學(xué)歡迎給我留言争涌,感謝大家能看到這里粉楚,碼字不容易辣恋,如果你覺得這篇文章對(duì)你有些許的幫助,請(qǐng)給個(gè)贊模软,謝謝大家伟骨!
歡迎關(guān)注我的微信公眾號(hào)“AndroidBook”,也可以掃描下面的二維碼來關(guān)注: