我是IT王魔王 這是個人第2篇IT原創android
嘗試寫出適合中級Android開發者的最好博客面試
有個小夥伴面試時碰到了一個問題:不給Activity的根視圖添加id,怎麼獲取到這個View?api
沒聽明白這個問題沒關係,我再給你們解釋一下bash
Activity的佈局文件以下ide
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvTest"
android:textSize="30sp"
android:textColor="#ffffff"
android:text="我是Activity中的佈局"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="@color/colorAccent" />
</RelativeLayout>
複製代碼
Activity中的代碼以下:佈局
public class ViewTreeMainActivity extends AppCompatActivity {
private static final String TAG = "ViewTreeMainActivity===";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_tree_main);
findViewById(R.id.tvTest).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO: 2020/6/1 獲取佈局中的根節點 ,即本demo 佈局文件中的RelativeLayout
}
});
}
}
複製代碼
注意看TODO中須要咱們實現的邏輯,就是面試官的問題。學習
若是你看懂問題了,而且有答案了,那麼你能夠點贊離開了。ui
若是你沒看懂問題,可是有答案了,我信了你的邪。this
若是你沒看懂問題,而且沒有答案,麻煩你再審閱一下這個問題。spa
若是你看懂問題了,可是沒有答案,那麼你應該繼續往下看
如今我揭曉答案,就兩行代碼
FrameLayout rootView = findViewById(android.R.id.content);
RelativeLayout relativeLayout = (LinearLayout) rootView.getChildAt(0);
複製代碼
若是你碰到的面試官特別水,你如今能夠抄抄做業,離開了。
記得點贊
悲哀的是,通常面試官會再來一句,爲何?
是啊,爲何id要寫android.R.id.content?FrameLayout從哪裏來的?
想知道這個因此然,咱們就得從Android中的View(控件)樹結構提及
先上圖
咱們不熟悉的是紅虛線上的結構,這部分也是咱們學習的重點。
簡單解釋一下:
一、每一個Activity中都有一個Window,Window用於顯示咱們的界面,Activity負責管理Window。 二、每一個Window都有一個根View--->DecorView。Window自身並不能顯示界面,Android中真正顯示畫面得靠View。
三、DecorView是一個FrameLayout,咱們Activity佈局文件就是添加到了這個DecorView中。
看不懂是吧?沒事,接着看
這個結構就像是這樣的:
Activity就是那些窗戶框,負責管理Window,例如開窗、關窗戶等等。
DecorView就是像是給每一個窗戶上貼了一張白紙,做用是讓咱們顯示真正的畫面
所謂的setContentView,就是把Activity佈局inflate成一個View以後,把這個View添加到DecorView中,就像是給上圖的白紙上貼了一個窗花(View)。
咱們的Activity佈局就是窗花。
換句話說,窗戶看起來長什麼樣子,是由窗戶上的窗花決定的。
由於窗戶自己是透明的
好好把這段屢屢,仔細看看,由於個人水平就能講到這個程度了。想再讓我講的通俗易懂點,你得再等兩年。
但我建議你如今仍是靜下心來看,萬一兩年後我轉行了呢?
咱們接着看
在這個結構中,咱們須要瞭解的是DecorView的結構
老規矩,先上圖
解釋一下這張圖什麼意思
DecorView自身是一個FrameLayout 這個FrameLayout中有一個LinearLayout 咱們知道LinearLayout不是水平就是垂直方向,這裏它是垂直方向的。
這個垂直方向的LinearLayout分爲上下兩部分,每一個部分都有每一個部分的做用
上面的部分是ActionBar
下面的部分是FrameLayout,這個FrameLayout是有id的,這個id是content
咱們在Activity中setContentView就是把Activity的佈局文件添加到了這個id爲content的FrameLayout中了。
由於id是content,因此咱們的api名稱設計成了setContentView
好了,來一張相對立體一點的圖給你們總結一下
上一段你歷來沒有見過的代碼
public class ViewTreeMainActivity extends AppCompatActivity {
private static final String TAG = "ViewTreeMainActivity===";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//把佈局文件inflate成一個View
View rootView = View.inflate(this,R.layout.activity_view_tree_main,null);
//獲取那個id爲content的FrameLayout
FrameLayout content = findViewById(android.R.id.content);
//把佈局文件添加到FrameLayout中
content.addView(rootView);
}
}
複製代碼
若是你學習態度足夠認真,那麼你應該把這段代碼敲一下,你會驚奇的發現,咱們的Activity依然能正常展現。
而事實上,setContentView作的也就是這麼一件事。
別翻源碼了,源碼裏不是這樣寫的。
感興趣的能夠本身去翻一下phoneWindow中setContentView的源碼。
如今咱們回過頭來看看文章開頭提到的面試題以及我給出的答案
不給Activity的根視圖添加id,怎麼獲取到這個View?
FrameLayout rootView = findViewById(android.R.id.content);
RelativeLayout relativeLayout = (LinearLayout) rootView.getChildAt(0);
複製代碼
如今你知道答案爲何這麼寫了吧?
最後還有幾個問題須要交代一下:
上面的圖中爲了讓你們更加容易理解,我用了一種錯誤的表達方式:Activity的佈局,這個是故意說的不嚴謹的,爲了防止有人擡槓,特此聲明一下。
ActionBar的是否顯示、Window的背景顏色都跟Activity的主題有關當,這點相信地球人都知道。
個人問題是ActionBar不顯示的時候,具體是怎麼操做的?難道是經過setVisiable(GONE)的方式隱藏的嗎?
咱們能夠經過代碼requestWindowFeature(Window.FEATURE_NO_TITLE)來動態隱藏Activity的ActionBar,不過這裏有一點須要注意,這行代碼須要寫在setContentView以前調用(這個我求你本身敲個demo試試吧)
個人問題是,爲何必定要寫在setContentView以前調用?
另外,這個結構其實並非足夠的細緻,都是爲了下降理解學習的難度(其實再細了我也不會) 等你掌握了這些內容後,之後看到別人寫的更加詳細的,接受起來纔會更加容易
等一下,再加一個問題,瞭解這些有什麼用呢?
欲知後事,且聽下回分解。