DialogFragment には表示するダイアログのインスタンスを自分で作成するための onCreateDialog() というメソッドが用意されています。
このメソッドを Override して、任意の Dialog クラスのインスタンスを返すことでオリジナルのテーマを適用したダイアログを表示することができます。
public class MyDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ダイアログの中に表示する View を返す
...
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity(), R.style.MyDialogTheme);
return dialog;
}
}
こうすると、MyDialogTheme を適用したダイアログになります。
とかすれば、タイトル部分のないダイアログにすることができます。
ダイアログのサイズを指定する方法で、この MyDialogFragment の大きさを指定しようとした場合、
public class MyDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ダイアログの中に表示する View を返す
...
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity(), R.style.MyDialogTheme);
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
DisplayMetrics metrics = getResources().getDisplayMetrics();
int dialogWidth = (int) (metrics.widthPixels * 0.8);
int dialogHeight = (int) (metrics.heightPixels * 0.8);
lp.width = dialogWidth;
lp.height = dialogHeight;
dialog.getWindow().setAttributes(lp);
return dialog;
}
}
としてもなぜか適用されません。
DialogFragment の実装をみると getLayoutInflater() でフィールドの mDialog に onCreateDialog() の戻り値を格納しています。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/app/DialogFragment.java#391
391 /** @hide */
392 @Override
393 public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
394 if (!mShowsDialog) {
395 return super.getLayoutInflater(savedInstanceState);
396 }
397
398 mDialog = onCreateDialog(savedInstanceState);
...
415 }
この getLayoutInflater() を呼び出す部分(FragmentManager)をみると
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/app/FragmentManager.java#795
795 f.mView = f.onCreateView(f.getLayoutInflater(f.mSavedFragmentState),
796 container, f.mSavedFragmentState);
getLayoutInflater() を呼び出して、その結果を引数として onCreateView() を呼んでいます。
この後に DialogFragment の onActivityCreated() が呼ばれ、ここで onCreateView() で返した View がダイアログに setContentView() でセットされます。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/app/DialogFragment.java#456
456 @Override
457 public void onActivityCreated(Bundle savedInstanceState) {
458 super.onActivityCreated(savedInstanceState);
459
460 if (!mShowsDialog) {
461 return;
462 }
463
464 View view = getView();
465 if (view != null) {
466 if (view.getParent() != null) {
467 throw new IllegalStateException("DialogFragment can not be attached to a container view");
468 }
469 mDialog.setContentView(view);
470 }
...
483 }
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/app/Fragment.java#1106
1106 public View getView() {
1107 return mView;
1108 }
つまり流れとしては
getLayoutInflater()
→ onCreateDialog()
↓
onCreateView()
↓
onActivityCreated()
のような順番になります。onCreateDialog() が一番最初です。
onActivityCreated() で Dialog の setContentView() が呼ばれるわけですが、Dialog の setContentView() は Window の setContentView() を呼び出しているだけです。
84 Window mWindow;
...
146 Dialog(Context context, int theme, boolean createContextWrapper) {
...
155 mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
156 Window w = PolicyManager.makeNewWindow(mContext);
157 mWindow = w;
...
163 }
...
466 public void setContentView(View view) {
467 mWindow.setContentView(view);
468 }
Window クラスの setContentView() は abstract メソッドで、
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/view/Window.java#922
922 public abstract void setContentView(View view);
実際は Window を継承した PhoneWindow などに実装があります。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java#264
258 @Override
259 public void setContentView(View view) {
260 setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
261 }
262
263 @Override
264 public void setContentView(View view, ViewGroup.LayoutParams params) {
265 if (mContentParent == null) {
266 installDecor();
267 } else {
268 mContentParent.removeAllViews();
269 }
270 mContentParent.addView(view, params);
271 final Callback cb = getCallback();
272 if (cb != null && !isDestroyed()) {
273 cb.onContentChanged();
274 }
275 }
ここの setContentView() で呼ばれる installDecor() で呼ばれる generateLayout() で
2738 private void installDecor() {
2739 if (mDecor == null) {
2740 mDecor = generateDecor();
2741 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
2742 mDecor.setIsRootNamespace(true);
2743 }
2744 if (mContentParent == null) {
2745 mContentParent = generateLayout(mDecor);
2746
...
2498 protected ViewGroup generateLayout(DecorView decor) {
2499 // Apply data from current theme.
2500
2501 TypedArray a = getWindowStyle();
2502
2503 if (false) {
2504 System.out.println("From style:");
2505 String s = "Attrs:";
2506 for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
2507 s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
2508 + a.getString(i);
2509 }
2510 System.out.println(s);
2511 }
2512
2513 mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
2514 int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
2515 & (~getForcedWindowFlags());
2516 if (mIsFloating) {
2517 setLayout(WRAP_CONTENT, WRAP_CONTENT);
2518 setFlags(0, flagsToUpdate);
2519 } else {
2520 setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
2521 }
<item name="android:windowIsFloating">true</item> がスタイルに定義されていた場合、setLayout(WRAP_CONTENT, WRAP_CONTENT); を呼んでいます。
http://tools.oesf.biz/android-4.0.1_r1.0/xref/frameworks/base/core/java/android/view/Window.java#604
604 public void setLayout(int width, int height) {
605 final WindowManager.LayoutParams attrs = getAttributes();
606 attrs.width = width;
607 attrs.height = height;
608 if (mCallback != null) {
609 mCallback.onWindowAttributesChanged(attrs);
610 }
611 }
ありましたー。そう、Dialog の setContentView() を呼び出すと、ここで WindowManager.LayoutParams に WRAP_CONTENT, WRAP_CONTENT がセットされてしまうのです。
ということで、意図通りにするには
public class MyDialogFragment extends DialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ダイアログの中に表示する View を返す
...
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Dialog dialog = getDialog();
WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
DisplayMetrics metrics = getResources().getDisplayMetrics();
int dialogWidth = (int) (metrics.widthPixels * 0.8);
int dialogHeight = (int) (metrics.heightPixels * 0.8);
lp.width = dialogWidth;
lp.height = dialogHeight;
dialog.getWindow().setAttributes(lp);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = new Dialog(getActivity(), R.style.MyDialogTheme);
return dialog;
}
のように super.onActivityCreated() を呼んだ後に getDialog() で Dialog のインスタンスを取得して、それに対して WindowManager.LayoutParams をセットすれば OK です!