android.app.Dialog(23)里window的那些事(坑)
Posted ihrthk
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了android.app.Dialog(23)里window的那些事(坑)相关的知识,希望对你有一定的参考价值。
不要使用theme去配置Dialog的gravity
由于现在手机的尺寸比较大(相对于智能机开始的3.5in、4.0in),而Dialog默认都是显示在屏幕中心的位置,用户触摸起来多不便。所以大多数产品都会要求Dialog在底部显示。
所以你可能这样写:
<style name="BottomDialog" parent="@android:style/Theme.Dialog">
<item name="android:gravity">bottom</item>
</style>
或者这样写:
dialog.getWindow().setGravity(Gravity.BOTTOM);
那么哪一种会更好一点呢?心里想,既然xml可以实现,那就用xml吧。毕竟xml有更好的配置性和维护性。
如果你真这样想的话,等你实践一下,就会发现。通过xml的方式配置Dialog的gravity,根本行不用。反而是第二种是可行的。
这不科学啊,但是你冷静一下,去仔细看一下Dialog的构造方法。原因并不复杂,你在style里配置的gravity属性,是没有用。都会被w.setGravity(Gravity.CENTER)所覆盖。
对这种实现方法,不发表评论,理解并记住就好。
//Dialog.java
public Dialog(@NonNull Context context)
this(context, 0, true);
public Dialog(@NonNull Context context, @StyleRes int themeResId)
this(context, themeResId, true);
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper)
if (createContextThemeWrapper)
if (themeResId == 0)
final TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true);
themeResId = outValue.resourceId;
mContext = new ContextThemeWrapper(context, themeResId);
else
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
需要调用setAttributes,而不是仅改变attributes
问题来了,如果在上一个问题中。我不用这个方法
dialog.getWindow().setGravity(Gravity.BOTTOM);
而改用
WindowManager.LayoutParams attributes = dialog.getWindow().getAttributes();
attributes.gravity = Gravity.BOTTOM;
//由于attributes是引用类型,所以不需要重写设置一遍,就可以改变它的值
//dialog.getWindow().setAttributes(attributes);
如果你也是这么想的,并且也这么做了,实际情况就是:它也行不用,但是你加上了setAttributes这个方法,就是好了。就在我不困惑不解的时候,我看下了setAttributes和setGravity的源码,我发现他们都不约而同地调用dispatchWindowAttributesChanged这个方法。
奥,真相大白:原来改变了attributes的里面的值,还要通知Window进行回调一下。
//Window.java
public void setAttributes(WindowManager.LayoutParams a)
mWindowAttributes.copyFrom(a);
dispatchWindowAttributesChanged(mWindowAttributes);
protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs)
if (mCallback != null)
mCallback.onWindowAttributesChanged(attrs);
反思:还是要尽量,直接使用系统提供的公开方法。这样会避免入坑。
setAttributes要在setContentView之前调用
这时候我们想要设置这个dialog充满屏幕的宽(也同是需要调用setBackgroundDrawableResource来设置Dialog的DecorView背景透明,并且没有边框)。可以参考如下代码来完成。乍一看,没有什么问题,但是当你运行一下。它也行不通
Dialog dialog = new Dialog(this, android.R.style.Theme_Dialog);
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
dialog.setContentView(R.layout.dialog);
dialog.show();
而需要把dialog.setContentView(R.layout.dialog);放在设置Window的前面才行。
Dialog dialog = new Dialog(this, android.R.style.Theme_Dialog);
dialog.setContentView(R.layout.dialog);
dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
dialog.show();
当我们读一下PhoneWindow的setContentView的源码,再结合一下onWindowAttributesChanged方法。就会恍然大悟,原来是这样。
哪样尼?就是回调onWindowAttributesChanged的时候,而mDecor根本没有创建出来,随意就直接return掉了。
//PhoneWindow.java
public void setContentView(int layoutResID)
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null)
installDecor();
else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS))
mContentParent.removeAllViews();
if (hasFeature(FEATURE_CONTENT_TRANSITIONS))
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
else
mLayoutInflater.inflate(layoutResID, mContentParent);
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed())
cb.onContentChanged();
//Dialog.java
public void onWindowAttributesChanged(WindowManager.LayoutParams params)
if (mDecor != null)
mWindowManager.updateViewLayout(mDecor, params);
最后:奇怪真是奇怪,目前只有setLayout有这个问题,而setBackgroundDrawableResource和setDimAmount都没有这个问题。欢迎在下面留言,告知一下。谢谢。
以上是关于android.app.Dialog(23)里window的那些事(坑)的主要内容,如果未能解决你的问题,请参考以下文章