标签:phonewindow
<p>转载自<a href="http://www.mamicode.com/blog.csdn.net/u013356254/article/details/55116259" target="_blank">blog.csdn.net/u013356254/article/details/55116259</a></p>
<p>android交流:364595326</p>
<ul>
<li>android中我们常见的Activity,Diaog等内部都封装了PhoneWindow对象。</li>
<li>
我们今天要探讨的是两个问题
<ul>
<li><strong>为什么系统在创建Acivity或者Dialog的时候封装了PhoneWindow对象,而我们自己写悬浮窗口的时候并没有使用PhoneWindow对象?</strong></li>
<li><strong>为什么Diaog封装了PhoneWindow对象,而PopupWindow却直接将contentView封装成PopupDecorView(FrameLayout子类),直接调用WM来添加view?</strong> </li>
</ul>
</li>
<li>
<ul>
<li>
<p>我们从Dialog的setContentView()方法说起。源码</p>
<pre><code> public void setContentView(@NonNull View view, @Nullable ViewGroup.LayoutParams params) {
// 调用的是window的方法
mWindow.setContentView(view, params);
}
</code></pre>
</li>
<li>
<p>下面是PhoneWindow的setContentView()方法。</p>
<pre><code>@Override
public void setContentView(int layoutResID ) {
// mContentParent是id为ID_ANDROID_CONTENT的FrameLayout
// 我们经常写的setContentView,这个方法,其实就是给id为ID_ANDROID_CONTENT的view添加一个孩子
if (mContentParent == null) {
// 下面这个方法。完成了两件事情
// 1 创建DecorView(FrameLayout),也就是我们经常说的window中有个DecorView对象。
// 2 给mContentParent赋值
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 如果没有5.0转场动画,remove掉之前添加的所有view
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
// 5.0专场动画
view.setLayoutParams(params);
final Scene newScene = new Scene(mContentParent, view);
transitionTo(newScene);
} else {
// 给id为ID_ANDROID_CONTENT的view添加新的孩子
// 将layoutResID添加到ContentParent上面
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
</code></pre>
</li>
<li>
<p>PhoneWindow.setContentView()方法的核心是,,生成DecorView和mContentParent对象,之后将布局文件添加到mContentParent上面去</p>
</li>
<li>
<p>接下来我们分析installDecor()方法</p>
<pre><code>private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 产生decorView 也就是ViewTree的根节点
mDecor = generateDecor(-1);
} else {
// 将decorView和window关联起来
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 根据decorview产生我们的ContentParent也就是id为content的viewGroup,
mContentParent = generateLayout(mDecor);
}
}
</code></pre>
</li>
<li>
<p>我们可以看到installDecor()方法主要是创建了DecorView,和mContentParent对象。</p>
</li>
<li>
<p>下面是generateDecor(-1)源码</p>
<pre><code> protected DecorView generateDecor(int featureId) {
// 创建DecorView(FrameLayout)对象,ViewTree的根节点
return new DecorView(context, featureId, this, getAttributes());
}
</code></pre>
</li>
<li>
<p>下面是创建mContentParent的代码</p>
<pre><code>protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
// 获得window的样式
TypedArray a = getWindowStyle();
/*省略掉一些设置样式的代码/
// 下面的代码是给decorView填充孩子的
// 主要功能是根据不同的配置给decorView添加不同的布局文件(即给decorView添加不同的孩子节点)
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);
// 设置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.
// 这里也说明了,我们经常写的requestWindowFeature(Window.FEATURE_NO_TITLE)代码为什么一定放在setContentView之前。
// 因为系统会根据配置找不同的布局文件,而一旦添加了布局文件,就没有办法再移除title了。因此会抛出异常
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 接下来是给赋值,这里直接调用的findViewById(),其实内部会调用decorView.findViewById();
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
</code></pre>
</li>
</ul>
</li>