對于接觸安卓開不到一年的自己來說,總結下view的生命周期還是有非常重要的好處的,不僅表達了對view的了解,也可以給初學者學習參考;本文就粗略總結下view的加載機制,上一文中給大家粗略介紹了下安卓的繪圖基礎和原理,也偶爾提到了安卓的view是通過解析xml,然後變成Java對象,再通過父類的canvas和paint繪制出來的,如果往上層了解,簡單的概括下,就是在activty裡通過這是ContenView方法,安卓WindownManger采用pul器l解析對應控件然後經過測量,擺放,最後繪制到界面上的,看了安卓關于view的源代碼,也不難解釋為何有的朋友在開發中擷取鍵盤高度或者某一控件高度擷取為零的原因,因為view在渲染時候并未測繪出來,而此時的實際高度必定為零。
View和ViewGroup
view在安卓中充當具體可見視圖東西,視圖組建,其可稱之做為ViewGroup的子類,填充到父容器中,ViewGruop是一組view的集合,用于存放和管理View的大小和具體位置功能,其可以了解為Activity和Fragmengt的關系,其兩者生命周期非常類似。安卓的五大布局都是ViewGroup的子類,一些常用控件都是View的子類。
view的周期如同就如建造一棟房子,我們首先需要籌備材料,然後進行實地測量,在進行具體挖地基,用準備的材料進行修建,最後傳遞人們居住。
一 View的大緻生命周期:
主要:接收XML完成,進行測量,擺放,繪制,綁定到activty中 其自生還有很多API在不同場景觸發。
[java] view plain copy print ?
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- @Override
- protected void onLayout(boolean changed, int left, int top, int right,
- int bottom) {
- // TODO Auto-generated method stub
- super.onLayout(changed, left, top, right, bottom);
- }
- @Override
- protected void onDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- }
- @Override
- protected void onFinishInflate() {
- // TODO Auto-generated method stub
- super.onFinishInflate();
- }
- @Override
- protected void onAttachedToWindow() {
- // TODO Auto-generated method stub
- super.onAttachedToWindow();
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
// TODO Auto-generated method stub
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
}
@Override
protected void onFinishInflate() {
// TODO Auto-generated method stub
super.onFinishInflate();
}
@Override
protected void onAttachedToWindow() {
// TODO Auto-generated method stub
super.onAttachedToWindow();
}
1 onFinshInFlate()
充當建房子時的材料接收準備,此材料收購任務傳遞 WindowManage.setContView進行實際加載和解析。
用來接收在avtity中指定的布局檔案,等待xml 解析器(pull)分别解析完所有子元素控件後觸發,用于view或者ViewGroup進行之後一些列工作
2 onMeasure()
充當實際選址,和繪制工程圖的作用。
用來實際測量子元素的寬高 ,其裡面子控件可以用Measue()方法來自我進行測繪,
[java] view plain copy print ?
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- int width = 400;
- int height = 800;
- measure(width, height);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
int width = 400;
int height = 800;
measure(width, height);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
如果在ViewGruop 我們可以對子布局依次進行測繪指派,其具體會調用measure進行測繪,其内部會觸發MeasureSpec.adjust轉換, setMeasuredDimension進行參數設定,最後又會觸發子View的onMeasure()的方法,使用遞歸進行輪訓測繪
知道沒有任何Child的是指定參數,用MeasureCache來儲存測好的值,便于後面的周期使用。
[java] view plain copy print ?
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // TODO Auto-generated method stub
- int width = widthMeasureSpec;
- int height = 0;
- for (int i = 0; i < getChildCount(); i++) {
- View child = getChildAt(i);
- //擷取子view的測量高度
- height = child.getMeasuredHeight();
- //擷取子view的測量寬度
- width = child.getMeasuredWidth();
- //進行指派測量值
- getChildAt(i).measure(width, height);
- height += height;
- }
- setMeasuredDimension(width, height);//當然我們可以直接設定寬高,無需調用父類onMessur的方法
- //super.onMeasure(width, height);
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
int width = widthMeasureSpec;
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//擷取子view的測量高度
height = child.getMeasuredHeight();
//擷取子view的測量寬度
width = child.getMeasuredWidth();
//進行指派測量值
getChildAt(i).measure(width, height);
height += height;
}
setMeasuredDimension(width, height);//當然我們可以直接設定寬高,無需調用父類onMessur的方法
//super.onMeasure(width, height);
}
3 onLayout()
充當更具工程圖進行完位址,決定房間位置的作用。
用來進行子控件的具體擺放位置,其和測量的方法都是已經子控件在螢幕右上角的位置開始計算。
[java] view plain copy print ?
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int left = 0;
- int top = 0;
- int right = 60;
- int bottom = 70;
- layout(l, t, r, b);
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int left = 0;
int top = 0;
int right = 60;
int bottom = 70;
layout(l, t, r, b);
}
[java] view plain copy print ?
- </pre><span style="font-size:14px"> 如果在Viewgroup中,和上面的測量方法一樣,依然采用周遊子控件,依次進行Layout(),到最後還依然遞歸到onLayout上來,因為不難了解,這裡不再解釋,值得強調的是,如果你在ViewGroup中重寫 onLayout(),不能在繼續super.onLayout()方法。因為ViewGruop已經沒有可以存放的父類了。</span><span style="font-size:18px">4 onDraw()</span><span style="font-size:14px"> 充當我們的房子的的切牆和粉刷工作。</span><p></p><p style="margin-top:0px; margin-bottom:1.1em; outline:none!important"><span style="font-size:14px"> 安卓中用來做我們測好已經擺放好view的繪制工作,上篇文章中講到,此方法結Canvas和Paint進行基礎繪制工作,如果自定義控件 這些繪制需要我們自己去繪制 父類的onDraw()為抽象類,具體繪制情況基于你繼承的父類控件類型(IamgeView,TextView等),而ViewGroup是有VIew特性的他是循環子類的onDraw()方法。這就解釋了我們如果不繼承任何類型的view,如果不重寫Ondraw方法,即使已經做了測繪和布局擺放,也無法顯示出來,因為系統無法識别你的view該調那個對應的ondraw()方法,其父類繪制源碼也未做任何處理。 </span> </p><pre name="code" class="java">@Override
- protected void onDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- Paint paint = new Paint();
- canvas.drawLine(startX, startY, stopX, stopY, paint);
- super.onDraw(canvas);
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
</pre><span style="font-size:14px"> 如果在Viewgroup中,和上面的測量方法一樣,依然采用周遊子控件,依次進行Layout(),到最後還依然遞歸到onLayout上來,因為不難了解,這裡不再解釋,值得強調的是,如果你在ViewGroup中重寫 onLayout(),不能在繼續super.onLayout()方法。因為ViewGruop已經沒有可以存放的父類了。</span><span style="font-size:18px">4 onDraw()</span><span style="font-size:14px"> 充當我們的房子的的切牆和粉刷工作。</span><p></p><p style="margin-top:0px; margin-bottom:1.1em; outline:none!important"><span style="font-size:14px"> 安卓中用來做我們測好已經擺放好view的繪制工作,上篇文章中講到,此方法結Canvas和Paint進行基礎繪制工作,如果自定義控件 這些繪制需要我們自己去繪制 父類的onDraw()為抽象類,具體繪制情況基于你繼承的父類控件類型(IamgeView,TextView等),而ViewGroup是有VIew特性的他是循環子類的onDraw()方法。這就解釋了我們如果不繼承任何類型的view,如果不重寫Ondraw方法,即使已經做了測繪和布局擺放,也無法顯示出來,因為系統無法識别你的view該調那個對應的ondraw()方法,其父類繪制源碼也未做任何處理。 </span> </p><pre name="code" class="java">@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
Paint paint = new Paint();
canvas.drawLine(startX, startY, stopX, stopY, paint);
super.onDraw(canvas);
}
其具體繪制圖形細節請參來篇UI的繪制機制。
5 onAttachedToWindow()
充當房子修建完成,傳遞我們居住了。
當以上所有工作完成之後,觸發此方法,用于綁定到setContView()的Activity上,此時生命周期交由Activity使用,但不代表view停止工作。onDetachedFromWindow()和此方法相反,當view被移除出去之後觸發。
二 view 的其他周期
以上是整個view必須觸發的方法,但是更多的方法未必會部觸發,接下來介紹下幾個不被人熟知的API
[java] view plain copy print ?
- @Override
- public void clearFocus() {
- // TODO Auto-generated method stub
- super.clearFocus();
- }
- @Override
- public void invalidate(int l, int t, int r, int b) {
- // TODO Auto-generated method stub
- super.invalidate(l, t, r, b);
- }
- @Override
- public void requestLayout() {
- // TODO Auto-generated method stub
- super.requestLayout();
- }
- @Override
- public void forceLayout() {
- // TODO Auto-generated method stub
- super.forceLayout();
- }
- @Override
- protected Parcelable onSaveInstanceState() {
- // TODO Auto-generated method stub
- return super.onSaveInstanceState();
- }
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- // TODO Auto-generated method stub
- super.onSizeChanged(w, h, oldw, oldh);
- }
- @Override
- protected void onAnimationStart() {
- // TODO Auto-generated method stub
- super.onAnimationStart();
- }
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- // TODO Auto-generated method stub
- super.onConfigurationChanged(newConfig);
- }
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5yc0VGcwlmbz9VZ2F2cvw1cldWYtl2LcRXZu5ibkN3Yuc2bsJmLjlGdhR3cvw1LcpDc0RHaiojIsJye.png)
@Override
public void clearFocus() {
// TODO Auto-generated method stub
super.clearFocus();
}
@Override
public void invalidate(int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.invalidate(l, t, r, b);
}
@Override
public void requestLayout() {
// TODO Auto-generated method stub
super.requestLayout();
}
@Override
public void forceLayout() {
// TODO Auto-generated method stub
super.forceLayout();
}
@Override
protected Parcelable onSaveInstanceState() {
// TODO Auto-generated method stub
return super.onSaveInstanceState();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
// TODO Auto-generated method stub
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected void onAnimationStart() {
// TODO Auto-generated method stub
super.onAnimationStart();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
// TODO Auto-generated method stub
super.onConfigurationChanged(newConfig);
}
1 invalidate()
此方法用來進行重繪工作,及時寬高和位置不變的情況也會可以主動調用。
2 requestLayout()
請求重新擺放,給予位置。
3 forceLayout()
清除已擺放的位置資料,釋放view的具體坐标。
4 onSizeChanged()
在view尺寸發生變化是觸發。一般父布局的大小不會發生變化的,手機螢幕固定
5 onConfigurationChanged()
橫豎屏切換之後觸發,此時view将從測繪,擺放和繪制重新走一遍。
6 onAnimationStart()
view有設定動畫是觸發,預設無動畫,此方法對目前view截取bitmap鏡像,不斷調用draw進行繪制,
7 onSaveInstanceState()
儲存目前的屬性狀态,便于切換view之前進行可序列傳輸,當我view不可見的時候,此時view的大小,位置和繪制的鏡像位圖,并未從記憶體中清除,當view再次顯示的時候觸發此方法。
到此大緻規程已經熟知,
當view執行onMeasure()是周遊到存在子類的時候就會調用子子類Measure()的方法,子類在繼續調用其onMeasure()方法,當其子類無子類的時候跳出交由父類繼續執行onMeasure()方法,依次遞歸,直到所有子類全部測量完畢為止進行onMeasure()後繼續onLayout,其如上圖一樣,繼續周遊子view,如果有子類存在調用其Layout,子類内部調用onLayout(),依次遞歸。直到所有控件全部布局完畢,後開啟ondraw()。
如下圖:
當viewGroup裡存在兩個子類,一個view3和ViewGroup2,這是就會調用其兩個子類的XXX方法,子類的XXX内部又會調用其子類的onXXx()。ViewGroup檢測到View存在的時候又會調用其View1,.XXX(),依次遞歸。
ps:當然子控件的坐标不是按螢幕左上角原點位置計算,而是按父親控件的左上角起點計算, 而事件中的觸摸坐标不管是哪個view都是按螢幕原點計算的.
通過以上步驟,我們可以總結出,view的加載過程其實就是一個不斷周遊其子節點再一次添加的過程,和其xml的解析如出一轍。歡迎大家閱讀。