解决办法在 onStart() 添加如下代码:
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Why?
然后就是思考,为什么放在 onStart 里面?。
1、首先是跟踪一下 setLayout 的调用路径。
public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
dispatchWindowAttributesChanged(attrs);
}
2、dispatchWindowAttributesChanged
protected void dispatchWindowAttributesChanged(WindowManager.LayoutParams attrs) {
if (mCallback != null) {
mCallback.onWindowAttributesChanged(attrs);
}
}
到这里有一个问题就出来了。 mCallback 是什么?
首先它是 Window.Callback 接口的对象。那这里的实现都是什么呢?查看 DialogFragment 中 Dialog 的创建过程,发现这里的 mCallback 其实就是 指Dialog 实例。如下:
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
// ...
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this); // mCallback 就是 Dialog 对象。
w.setWindowManager(mWindowManager, null, null);
w.setGravity(Gravity.CENTER);
// ...
}
3、onWindowAttributesChanged
@Override
public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
if (mDecor != null) {
mWindowManager.updateViewLayout(mDecor, params);
}
}
4、通过搜索发现 mDecor 是在 Dialog#show 方法中创建。
public void show(){
// ...
mDecor = mWindow.getDecorView();
// ...
mWindowManager.addView(mDecor, l);
}
5、DialogFragment#show
所以接下来要分析 DialogFragment 来分析是什么地方让 setLayout 在 onCreateView 中执行与 onStart 执行的差别。
通过 DialogFragment 是先通过调用其 show 方法来显示的,而它是一个 Fragment 所以其实是通过 Fragment 的生命周期来显示的, Fragment 的生命周期在 onStart 前的有如: onAttach,onCreate,onCreateView,onActivityCreated,onStart
public void show(FragmentManager manager, String tag) {
mDismissed = false;
mShownByMe = true;
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commit();
}
onCreateDialog 是在 onCreateView 之前调用的。所以上面的流程中,setLayout 是,mCallback 已经不为空了。 上面的流程判断就只差 mDecor 的判断了。通过上面的分析,我们已经知道 mDecor 是在 Dialog#show 方法中创建的。而 Dialog#show 方法是在 DialogFragment#onStart 方法中创建的。
public void onStart() {
super.onStart();
if (mDialog != null) {
mViewDestroyed = false;
mDialog.show();
}
}
所以我们的 setLayout 是需要在 DialogFragment#onStart 调用之后才会实际的生效。即调用 mWindowManager.updateViewLayout(mDecor, params);