轉(zhuǎn)載自blog.csdn.net/u013356254/article/details/55116259
android交流:364595326
android中我們常見(jiàn)的Activity,Diaog等內(nèi)部都封裝了PhoneWindow對(duì)象。
我們今天要探討的是兩個(gè)問(wèn)題
為什么系統(tǒng)在創(chuàng)建Acivity或者Dialog的時(shí)候封裝了PhoneWindow對(duì)象,而我們自己寫懸浮窗口的時(shí)候并沒(méi)有使用PhoneWindow對(duì)象?
為什么Diaog封裝了PhoneWindow對(duì)象,而PopupWindow卻直接將contentView封裝成PopupDecorView(FrameLayout子類),直接調(diào)用WM來(lái)添加view?
我們從Dialog的setContentView()方法說(shuō)起。源碼
public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
// 調(diào)用的是window的方法
mWindow.setContentView(view, params);
}
下面是PhoneWindow的setContentView()方法。
@Override
public void setContentView(int layoutResID ) {
// mContentParent是id為ID_ANDROID_CONTENT的FrameLayout
// 我們經(jīng)常寫的setContentView,這個(gè)方法,其實(shí)就是給id為ID_ANDROID_CONTENT的view添加一個(gè)孩子
if (mContentParent == null) {
// 下面這個(gè)方法。完成了兩件事情
// 1 創(chuàng)建DecorView(FrameLayout),也就是我們經(jīng)常說(shuō)的window中有個(gè)DecorView對(duì)象。
// 2 給mContentParent賦值
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 如果沒(méi)有5.0轉(zhuǎn)場(chǎng)動(dòng)畫,remove掉之前添加的所有view
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//? 5.0專場(chǎng)動(dòng)畫
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
// 給id為ID_ANDROID_CONTENT的view添加新的孩子
// 將layoutResID添加到ContentParent上面
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
PhoneWindow.setContentView()方法的核心是,生成DecorView和mContentParent對(duì)象,之后將布局文件添加到mContentParent上面去
接下來(lái)我們分析installDecor()方法
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 產(chǎn)生decorView 也就是ViewTree的根節(jié)點(diǎn)
mDecor = generateDecor(-1);
} else {
// 將decorView和window關(guān)聯(lián)起來(lái)
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 根據(jù)decorview產(chǎn)生我們的ContentParent也就是id為content的viewGroup,
mContentParent = generateLayout(mDecor);
}
}
我們可以看到installDecor()方法主要是創(chuàng)建了DecorView,和mContentParent對(duì)象。
下面是generateDecor(-1)源碼
protected DecorView generateDecor(int featureId) {
// 創(chuàng)建DecorView(FrameLayout)對(duì)象,ViewTree的根節(jié)點(diǎn)
return new DecorView(context, featureId, this, getAttributes());
}
下面是創(chuàng)建mContentParent的代碼
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 獲得window的樣式
TypedArray a = getWindowStyle();
/*省略掉一些設(shè)置樣式的代碼/
// 下面的代碼是給decorView填充孩子的
// 主要功能是根據(jù)不同的配置給decorView添加不同的布局文件(即給decorView添加不同的孩子節(jié)點(diǎn))
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
// System.out.println("Progress!");
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// 設(shè)置notitle的布局文件
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// Dialog樣式的
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
// System.out.println("Simple!");
}
mDecor.startChanging();
// 下面的方法是將找到的不同的布局文件,添加給decorView.
// 這里也說(shuō)明了,我們經(jīng)常寫的requestWindowFeature(Window.FEATURE_NO_TITLE)代碼為什么一定放在setContentView之前。
// 因?yàn)橄到y(tǒng)會(huì)根據(jù)配置找不同的布局文件,而一旦添加了布局文件,就沒(méi)有辦法再移除title了。因此會(huì)拋出異常
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 接下來(lái)是給賦值,這里直接調(diào)用的findViewById(),其實(shí)內(nèi)部會(huì)調(diào)用decorView.findViewById();
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
generateLayout(DecorView decor) 主要完成了兩件事,1通過(guò)不同的配置給decorView添加不同layoutResource布局文件, 2找到id為IDANDROIDCONTENT的view。
分析完setContentView代碼,我們發(fā)現(xiàn)setContentView.其實(shí)是將view添加到PhoneWindow的成員變量DecorView中的id為IDCONTENTANDROID的View節(jié)點(diǎn)上。還發(fā)現(xiàn)了DecorView的孩子節(jié)點(diǎn)會(huì)根據(jù)我們的requestWindowFeature()的不同,添加不同的layoutResource布局文件,而這些不同的layoutResource布局文件都是一個(gè)id為IDANDROIDCONTENT的孩子。
接下來(lái)我們分析Diaog的show()方法
public void show() {
// 拿到PhoneWindow中的decorView對(duì)象
mDecor = mWindow.getDecorView();
// 產(chǎn)生布局參數(shù)
WindowManager.LayoutParams l = mWindow.getAttributes();
if ((l.softInputMode
& WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION) == 0) {
WindowManager.LayoutParams nl = new WindowManager.LayoutParams();
nl.copyFrom(l);
nl.softInputMode |=
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
l = nl;
}
// wm添加decorView
mWindowManager.addView(mDecor, l);
}
我們發(fā)現(xiàn),寫到最后show()方法其實(shí)就是將decorView添加到wm中
而我們寫懸浮窗口的時(shí)候,直接用wm添加view。通過(guò)以上分析我們可以得出以下結(jié)論
結(jié)論
PhoneWindow的一個(gè)作用是給view包裹上一層DecorView。而DecorView中的布局結(jié)構(gòu),會(huì)根據(jù)requestWindowFeature()的不同而不同(requestWindowFeature()方法,會(huì)影響DecorView的孩子節(jié)點(diǎn)(layoutResource布局文件))
我們的Activity和Dialog的布局都比較復(fù)雜,比如都可能有appbar(toolbar/actionbar)等。因此通過(guò)PhoneWindow來(lái)封裝下可以更好的解耦代碼
PopupWindow或者Toast的布局比較簡(jiǎn)單。因此沒(méi)有必要包裹一層PhoneWindow。在源碼中也沒(méi)有發(fā)現(xiàn)有PhoneWindow的痕跡。