源碼基于8.0
oncreate()獲取view的寬高為0
activity啟動流程(http://www.reibang.com/p/4a20d9d68482
)桥温,最后一步,ams通過binder機制向app進程發(fā)送attachapplication請求弯院,然后app收到請求后乃秀,通過handler向主線程發(fā)送LAUNCH_ACTIVITY,然后執(zhí)行handleLaunchActivity()抄课。
1.performLaunchActivity(調用oncreate onstart)
2.handleResumeActivity(調用onresume-->window.addview())
經過一系列調用,最后走到ViewRootImpl的performTraversals()方法(之后流程見圖view的繪制流程.png)愧旦,開始view的measure,layout碘举,draw流程忘瓦。(來到這順便也可以通過源碼發(fā)現requestLayout,invalidate的區(qū)別)所以,在oncreate方法中耕皮,view是還沒有測量的境蜕,獲取的寬高為0。下面代碼段注釋1-2處凌停。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
//===注釋1:調用activity的聲名周期onresume()====
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished);
final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
boolean willBeVisible = !a.mStartedActivity;
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
}
}
if (r.window == null && !a.mFinished && willBeVisible) {
//===注釋2:獲取activity的window粱年,decoerview為window的內部類,
//也是view的根部局罚拟。通過wm.addview方法台诗,將ViewRootImpl對象,
//然后調用其setView()方法赐俗。其中setView()方法經過一些列折騰拉队,最終
//調用了performTraversals()方法。開始view的測量布局繪制流程阻逮。
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
//以下代碼略
}
measure流程
measure粱快,測量。確定view的大小叔扼。
調用measure()方法事哭,進行一些邏輯處理,然后調用onMeasure()方法瓜富,在其中調用setMeasuredDimension()設定View的寬高信息鳍咱,完成View的測量操作。
(注 MeasureSpec如何確定与柑,這里不就提了谤辜,需要注意的是,當自定義view時仅胞,需重寫measure()每辟,以區(qū)別wrap_content和match_parent)
layout流程
layout流程中,viewgroup需要根據布局的特性來重寫onlayout方法干旧。
draw流程
public void draw(Canvas canvas) {
...
int saveCount;
if (!dirtyOpaque) {
// 步驟1: 繪制本身View背景
drawBackground(canvas);
}
// 如果有必要,就保存圖層(還有一個復原圖層)
// 優(yōu)化技巧:
// 當不需要繪制 Layer 時妹蔽,“保存圖層“和“復原圖層“這兩步會跳過
// 因此在繪制的時候椎眯,節(jié)省 layer 可以提高繪制效率
final int viewFlags = mViewFlags;
if (!verticalEdges && !horizontalEdges) {
if (!dirtyOpaque)
// 步驟2:繪制本身View內容 默認為空實現, 自定義View時需要進行復寫
onDraw(canvas);
......
// 步驟3:繪制子View 默認為空實現 單一View中不需要實現胳岂,ViewGroup中已經實現該方法
dispatchDraw(canvas);
........
// 步驟4:繪制滑動條和前景色等等
onDrawScrollBars(canvas);
..........
return;
}
...
}
view和viewgroup都必須重寫ondraw方法來完成view的繪制流程编整。
總結
實現自定義view的話,根據具體需要實現的效果乳丰,正確重寫onMeasure掌测,onlayout,ondraw方法即可产园。
最后汞斧,附上開發(fā)過程中偶爾上傳過github的demo https://github.com/liujiyi/UIDemo
參考:android開發(fā)藝術探索第四章
博客:http://www.reibang.com/p/3d2c49315d68