前言java
關於android繪製流程的分析林林總總,雖然看過不少博客,但記住的東西卻屈指可數。閱讀別人寫的文章遠沒有本身去捋一遍思路來的記憶深入。寫文章的過程是幫助本身思考的過程,在思考的過程當中就會造成沉澱,從而達到技能的提高。android
學習應該是從總體到局部的,一頭扎到代碼中去每每會只見樹木不見森林,因此我們今天就來先見見深林,噢 我不是說半z深林,首先看一下window總體結構,以下圖所示:windows
佈局最外層是decorview,decorview和window的關係是:window有一個decorview。PhoneWindow是window的惟一實現類。decorview中是一個LinearLayout,其中包括一個viewstub和framlayout;framelayout中添加了subdecor, subdecor是一個viewgroup;subdecor中添加了一個linerlayout,包括一個actionbarcontainer和一個framlayout即上圖的content,在activity oncreate中設置的佈局將添加到content中。bash
對整體有過認識以後,就能夠看一下具體的實現過程。咱們將從外到內逐層分析其實現程, app
在ActivityThread的performLaunchActivity調用activity的attach方法,在attach方法中建立了PhoneWindow,以下所示ide
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
...
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback);
...
}
//在Activity中 新建phonewindow 一個activity對應一個phonewindow
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,
Window window, ActivityConfigCallback activityConfigCallback) {
...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
}複製代碼
public void setContentView(int resId){
//添加subdecor
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//添加本身寫的佈局
LayoutInflater.from(mContext).inflate(resId, contentParent);//在subdecor的content添加咱們的佈局
mOriginalWindowCallback.onContentChanged();
}複製代碼
在
ensureSubDecor()方法中,
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
//若是未添加過subdecor則實例化一個
mSubDecor = createSubDecor();
...
}
}
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
...
// Now let's make sure that the Window has installed its decor by retrieving it mWindow.getDecorView(); final LayoutInflater inflater = LayoutInflater.from(mContext); ViewGroup subDecor = null; //更具設置的不一樣feature,inflate subdecor if (!mWindowNoTitle) { if (mIsFloating) { // If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if
.....
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
}
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById(
R.id.action_bar_activity_content);//subdecor的content
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to // migrate them to our content view while (windowContentView.getChildCount() > 0) { final View child = windowContentView.getChildAt(0); windowContentView.removeViewAt(0); contentView.addView(child); } // Change our content FrameLayout to use the android.R.id.content id. // Useful for fragments. windowContentView.setId(View.NO_ID); contentView.setId(android.R.id.content); } // Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
return subDecor;
}
複製代碼
具體看一下啊
mWindow.getDecorView()方法,簡單的調用了installDecor(),若是decorView爲null則建立一個,添加布局並將佈局中的framelayout設置給
mContentParent ,在添加subdecor的時候將用到,至此decorview添加完成。
public class PhoneWindow extends Window -> public final View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
}
}
protected DecorView generateDecor(int featureId) {
....
return new DecorView(context, featureId, this, getAttributes()); //public class DecorView extends FrameLayout
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
....
WindowManager.LayoutParams params = getAttributes();
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
....
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//decorview佈局中的framelayout 即爲subdecor的容器
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view"); }
mDecor.finishChanging();
return contentParent;
}複製代碼
在第二部的createSubDecor() 方法中,添加了subdecor,具體以下佈局
private ViewGroup createSubDecor() {
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
...
// Now let's make sure that the Window has installed its decor by retrieving it mWindow.getDecorView(); final LayoutInflater inflater = LayoutInflater.from(mContext); ViewGroup subDecor = null; 1.//更具設置的不一樣feature,inflate subdecor if (!mWindowNoTitle) { if (mIsFloating) { // If we're floating, inflate the dialog title decor
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// Floating windows can never have an action bar, reset the flags
mHasActionBar = mOverlayActionBar = false;
} else if
.....
} else {
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
}
}
2.//subdecor的content
final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById( R.id.action_bar_activity_content);
final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content);
if (windowContentView != null) {
// There might be Views already added to the Window's content view so we need to // migrate them to our content view while (windowContentView.getChildCount() > 0) { final View child = windowContentView.getChildAt(0); windowContentView.removeViewAt(0); contentView.addView(child); } // Change our content FrameLayout to use the android.R.id.content id. // Useful for fragments. windowContentView.setId(View.NO_ID); //將subdecor中的framelayout id設置爲content contentView.setId(android.R.id.content); } 3.// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
return subDecor;
}複製代碼
具體看一下
mWindow.setContentView(subDecor)方法,實如今phonewindow中,在mContentParent 中添加subdecor,mContentParent爲decorview中的framelayout至此subdecor添加完成
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
mContentParent.addView(view, params);
}複製代碼
public void setContentView(int resId){
//添加subdecor
ensureSubDecor();
//找到content容器
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
//添加本身寫的佈局
LayoutInflater.from(mContext).inflate(resId, contentParent);//在subdecor的content添加咱們的佈局
mOriginalWindowCallback.onContentChanged();
}複製代碼
至此,整個window 佈局添加完成,以後即是將佈局繪製到屏幕上。學習