- dialog作为与用户交互最简单的方式之一,不能随意放位置,不能全屏确实很恶心。有网友这样写
Dialog dialog = new Dialog(this, R.style.Dialog);
dialog.show();
LayoutInflater inflater = LayoutInflater.from(this);
View viewDialog = inflater.inflate(R.layout.adapter_list, null);
Display display = this.getWindowManager().getDefaultDisplay();
int width = display.getWidth();
int height = display.getHeight();
//设置dialog的宽高为屏幕的宽高
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(width, height);
dialog.setContentView(viewDialog, layoutParams);
这样可以是可以,但是setContentView 要放到show之后,我经常show这个dialog的话就显得代码沉余,很不爽。
- 为什么要放到setContentView 之后呢,看dialog的构造方法,当没有传入themeid的时候用的是弹出此dialog的context 否则 ContextThemeWrapper包装themeresid 产生新的 mContext ,mContext 的重要性后面讲。
Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {
if (createContextThemeWrapper) {
if (themeResId == Resources.ID_NULL) {
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);
//这里创建了PhoneWindow
final Window w = new PhoneWindow(mContext);
mWindow = w;
w.setCallback(this);
w.setOnWindowDismissedCallback(this);
w.setOnWindowSwipeDismissedCallback(() -> {
if (mCancelable) {
cancel();
}
});
w.setWindowManager(mWindowManager, null, null);
//这里是dialog居中显示的原因,所以我们要改变他的位置就要在super()之后
w.setGravity(Gravity.CENTER);
mListenersHandler = new ListenersHandler(this);
}
再看dialog的setContentView 方法,有人把它放到show之前,有人把它放到show之后,上面代码我们看到mWindow 就是 用context创建的PhoneWindow
/**
* Set the screen content from a layout resource. The resource will be
* inflated, adding all top-level views to the screen.
*
* @param layoutResID Resource ID to be inflated.
*/
public void setContentView(@LayoutRes int layoutResID) {
mWindow.setContentView(layoutResID);
}
看Phonewindow的源码
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();
}
mContentParentExplicitlySet = true;
}
// installDecor 的时候会创建一个根布局,所以这个dialog能不能全屏也是它决定的
private void installDecor() {
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = 0; i < R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
// 如果window的属性windowIsFloating 表示是否悬浮
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
......
.....
}
public void setLayout(int width, int height) {
final WindowManager.LayoutParams attrs = getAttributes();
attrs.width = width;
attrs.height = height;
dispatchWindowAttributesChanged(attrs);
}
- 上面的那一些都是你调用setContentView的时候完成设置的。 setLayout(WRAP_CONTENT, WRAP_CONTENT)决定了你的dialog只能由子view的大小决定。当然这样你也可以让dialog接近全屏{ 可以设置你view的大小接近屏幕宽高 },你也可以在setContentView之后设置宽高{你要考虑时机,也很容易出bug }。
终极解决方案 看dialog的style 以及注释就明白了:
<!-- Default theme for dialog windows and activities (on API level 10 and lower),
which is used by the
{@link android.app.Dialog} class. This changes the window to be
floating (not fill the entire screen), and puts a frame around its
contents. You can set this theme on an activity if you would like to
make an activity that looks like a Dialog. -->
<style name="Theme.Dialog">
<item name="windowFrame">@null</item>
<item name="windowTitleStyle">@style/DialogWindowTitle</item>
<item name="windowBackground">@drawable/panel_background</item>
<item name="windowIsFloating">true</item>
结合源码我们知道只需要这样就可以自由设置宽高了
<item name="windowIsFloating">false</item>
dialog方便在哪:属于四大组件,不需要manifest注册;可以在任何地方弹
- 最后提一下Dialog第二次无法弹出的现象,解决方案:
if(mDialog!=null) {
if (D.BUG) Log.d(TAG, "mDialog.isShowing()2="+mDialog.isShowing()+",mShowing="+mShowing);
if(!mShowing){
if(mDialog.isShowing()) {
mDialog.dismiss();
}
mDialog = new VomeDialog(mContext);
}
}
mDialog.show();
网友评论