UPDATE 2021
For Kotlin users, I've crafted a couple of simple extension methods that will set the width of your DialogFragment
to either a percentage of the screen width, or near full screen:
/**
* Call this method (in onActivityCreated or later) to set
* the width of the dialog to a percentage of the current
* screen width.
*/
fun DialogFragment.setWidthPercent(percentage: Int) {
val percent = percentage.toFloat() / 100
val dm = Resources.getSystem().displayMetrics
val rect = dm.run { Rect(0, 0, widthPixels, heightPixels) }
val percentWidth = rect.width() * percent
dialog?.window?.setLayout(percentWidth.toInt(), ViewGroup.LayoutParams.WRAP_CONTENT)
}
/**
* Call this method (in onActivityCreated or later)
* to make the dialog near-full screen.
*/
fun DialogFragment.setFullScreen() {
dialog?.window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
Then in your DialogFragment
in or after onActivityCreated
:
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
setWidthPercent(85)
}
Consider the remainder of this answer for posterity.
DialogFragment
LayoutsIt's sort of mind numbing really.
When creating a DialogFragment
, you can choose to override onCreateView
(which passes a ViewGroup
to attach your .xml layout to) or onCreateDialog
, which does not.
You mustn't override both methods tho, because you will very likely confuse Android as to when or if your dialog's layout was inflated! WTF?
The choice of whether to override OnCreateDialog
or OnCreateView
depends on how you intend to use the dialog.
OnCreateDialog
.OnCreateView
.This is possibly the worst thing in the world.
onCreateDialog
InsanitySo, you're overriding onCreateDialog
in your DialogFragment
to create a customized instance of AlertDialog
to display in a window. Cool. But remember, onCreateDialog
receives no ViewGroup
to attach your custom .xml layout to. No problem, you simply pass null
to the inflate
method.
Let the madness begin.
When you override onCreateDialog
, Android COMPLETELY IGNORES several attributes of the root node of the .xml Layout you inflate. This includes, but probably isn't limited to:
background_color
layout_gravity
layout_width
layout_height
This is almost comical, as you are required to set the
layout_width
andlayout_height
of EVERY .xml Layout or Android Studio will slap you with a nice little red badge of shame.
Just the word DialogFragment
makes me want to puke. I could write a novel filled with Android gotchas and snafus, but this one is one of the most insideous.
To return to sanity, first, we declare a style to restore JUST the background_color
and layout_gravity
we expect:
<style name="MyAlertDialog" parent="Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:layout_gravity">center</item>
</style>
The style above inherits from the base theme for Dialogs (in the AppCompat
theme in this example).
Next, we apply the style programmatically to put back the values Android just tossed aside and to restore the standard AlertDialog
look and feel:
public class MyDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
View layout = getActivity().getLayoutInflater().inflate(R.layout.my_dialog_layout, null, false);
assert layout != null;
//build the alert dialog child of this fragment
AlertDialog.Builder b = new AlertDialog.Builder(getActivity());
//restore the background_color and layout_gravity that Android strips
b.getContext().getTheme().applyStyle(R.style.MyAlertDialog, true);
b.setView(layout);
return b.create();
}
}
The code above will make your AlertDialog
look like an AlertDialog
again. Maybe this is good enough.
If you're looking to set a SPECIFIC layout_width
or layout_height
for your AlertDialog
when it's shown (very likely), then guess what, you ain't done yet!
The hilarity continues as you realize that if you attempt to set a specific layout_width
or layout_height
in your fancy new style, Android will completely ignore that, too!:
<style name="MyAlertDialog" parent="Theme.AppCompat.Dialog">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:layout_gravity">center</item>
<!-- NOPE!!!!! --->
<item name="android:layout_width">200dp</item>
<!-- NOPE!!!!! --->
<item name="android:layout_height">200dp</item>
</style>
To set a SPECIFIC window width or height, you get to head on over to a whole 'nuther method and deal with LayoutParams
:
@Override
public void onResume() {
super.onResume();
Window window = getDialog().getWindow();
if(window == null) return;
WindowManager.LayoutParams params = window.getAttributes();
params.width = 400;
params.height = 400;
window.setAttributes(params);
}
Many folks follow Android's bad example of casting
WindowManager.LayoutParams
up to the more generalViewGroup.LayoutParams
, only to turn right around and castViewGroup.LayoutParams
back down toWindowManager.LayoutParams
a few lines later. Effective Java be damned, that unnecessary casting offers NOTHING other than making the code even harder to decipher.
Side note: There are some TWENTY repetitions of
LayoutParams
across the Android SDK - a perfect example of radically poor design.
For DialogFragment
s that override onCreateDialog
:
AlertDialog
look and feel, create a style that sets background_color
= transparent
and layout_gravity
= center
and apply that style in onCreateDialog
.layout_width
and/or layout_height
, do it programmatically in onResume
with LayoutParams