假设我在一个名为my_dialog_fragment.xml的xml布局文件中指定了我的DialogFragment的布局,并且我将其根视图的layout_width和layout_height值指定为固定值(例如100dp)。然后我在我的DialogFragment的onCreateView(…)方法中膨胀这个布局,如下所示:

View view = inflater.inflate(R.layout.my_dialog_fragment, container, false);

可悲的是,我发现当我的DialogFragment出现时,它不尊重layout_width和layout_height值在其xml布局文件中指定,而是根据其内容收缩或展开。有人知道我是否或如何得到我的DialogFragment尊重layout_width和layout_height值指定在其xml布局文件?目前,我必须在我的DialogFragment的onResume()方法中再次指定对话框的宽度和高度,如下所示:

getDialog().getWindow().setLayout(width, height);

这样做的问题是,我必须记住将来在两个地方对宽度和高度进行任何更改。


当前回答

@Override
public void onStart() {
    super.onStart();
    Dialog dialog = getDialog();
    if (dialog != null)
    {
        dialog.getWindow().setLayout(-1, -2);
        dialog.getWindow().getAttributes().windowAnimations = R.style.DialogAnimation;
        Window window = getDialog().getWindow();
        WindowManager.LayoutParams params = window.getAttributes();
        params.dimAmount = 1.0f;
        window.setAttributes(params);
        window.setBackgroundDrawableResource(android.R.color.transparent);
    }
}

其他回答

控制你的DialogFragment的宽度和高度的一种方法是确保它的对话框尊重你的视图的宽度和高度,如果它们的值是WRAP_CONTENT。

使用ThemeOverlay.AppCompat.Dialog

实现这一点的一个简单方法是使用Android支持库中包含的themeoverlay . appcompatc . dialog样式。

使用Dialog的DialogFragment:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(R.layout.dialog_view, null);

    Dialog dialog = new Dialog(getContext(), R.style.ThemeOverlay_AppCompat_Dialog);
    dialog.setContentView(view);
    return dialog;
}

AlertDialog(警告:minHeight="48dp"):

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(R.layout.dialog_view, null);

    AlertDialog.Builder builder = new AlertDialog.Builder(getContext(), R.style.ThemeOverlay_AppCompat_Dialog);
    builder.setView(view);
    return builder.create();
}

你也可以在创建对话框时将themeoverlay . appcompat_dialog设置为默认主题,将其添加到应用程序的xml主题中。 请注意,许多对话框确实需要默认的最小宽度才能看起来更好。

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <!-- For Android Dialog. -->
    <item name="android:dialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>

    <!-- For Android AlertDialog. -->
    <item name="android:alertDialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>

    <!-- For AppCompat AlertDialog. -->
    <item name="alertDialogTheme">@style/ThemeOverlay.AppCompat.Dialog</item>

    <!-- Other attributes. -->
</style>

使用Dialog的DialogFragment,使用android:dialogTheme:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(R.layout.dialog_view, null);

    Dialog dialog = new Dialog(getContext());
    dialog.setContentView(view);
    return dialog;
}

AlertDialog的对话片段,使用android:alertDialogTheme或alertDialogTheme(警告:minHeight="48dp"):

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(R.layout.dialog_view, null);

    AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
    builder.setView(view);
    return builder.create();
}

奖金

在旧的Android api上,对话框似乎有一些宽度问题,因为它们的标题(即使你没有设置一个)。 如果你不想使用themeoverlay . appcompata .Dialog样式,并且你的对话框不需要标题(或者有自定义标题),你可能想要禁用它:

@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    LayoutInflater inflater = LayoutInflater.from(getContext());
    View view = inflater.inflate(R.layout.dialog_view, null);

    Dialog dialog = new Dialog(getContext());
    dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
    dialog.setContentView(view);
    return dialog;
}

过时的答案,在大多数情况下都行不通

我试图让对话框尊重我的布局的宽度和高度,而不是通过编程指定固定的大小。

我认为android:windowMinWidthMinor和android:windowMinWidthMajor导致了这个问题。即使它们没有包含在我的活动或对话的主题中,它们仍然以某种方式应用到活动主题中。

我想到了三个可能的解决方案。

解决方案1:创建一个自定义对话主题,并在DialogFragment中创建对话时使用它。

<style name="Theme.Material.Light.Dialog.NoMinWidth" parent="android:Theme.Material.Light.Dialog">
    <item name="android:windowMinWidthMinor">0dip</item>
    <item name="android:windowMinWidthMajor">0dip</item>
</style>
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new Dialog(getActivity(), R.style.Theme_Material_Light_Dialog_NoMinWidth);
}

解决方案2:创建一个用于ContextThemeWrapper的自定义主题,该主题将作为对话框的上下文。如果你不想创建一个自定义对话框主题(例如,当你想使用android:dialogTheme指定的主题时),使用这个。

<style name="Theme.Window.NoMinWidth" parent="">
    <item name="android:windowMinWidthMinor">0dip</item>
    <item name="android:windowMinWidthMajor">0dip</item>
</style>
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    return new Dialog(new ContextThemeWrapper(getActivity(), R.style.Theme_Window_NoMinWidth), getTheme());
}

解决方案3(与AlertDialog):强制android:windowMinWidthMinor和android:windowMinWidthMajor到ContextThemeWrapper创建的AlertDialog$Builder。

<style name="Theme.Window.NoMinWidth" parent="">
    <item name="android:windowMinWidthMinor">0dip</item>
    <item name="android:windowMinWidthMajor">0dip</item>
</style>
@Override
public final Dialog onCreateDialog(Bundle savedInstanceState) {
    View view = new View(); // Inflate your view here.
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setView(view);
    // Make sure the dialog width works as WRAP_CONTENT.
    builder.getContext().getTheme().applyStyle(R.style.Theme_Window_NoMinWidth, true);
    return builder.create();
}

Working on Android 6.0, ran into the same issue. AlertDialog would default to predefined width set in the theme regardless of the actual width set in the custom view's root Layout. I was able to get it to set properly adjusting the width of the loading_message TextView. Without investigating further, it seems that sizing the actual elements and having the root Layout wrap around them makes it work as expected. Below is an XML layout of a loading dialog which sets width of the the dialog correctly. Using the this library for the animation.

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/custom_color"
    android:padding="@dimen/custom_dimen">
    <com.github.rahatarmanahmed.cpv.CircularProgressView
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/progress_view"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_centerHorizontal="true"
        app:cpv_color="@color/white"
        app:cpv_animAutostart="true"
        app:cpv_indeterminate="true" />
    <TextView
        android:id="@+id/loading_message"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_below="@+id/progress_view"
        android:layout_centerHorizontal="true"
        android:gravity="center"
        android:textSize="18dp"
        android:layout_marginTop="@dimen/custom_dimen"
        android:textColor="@color/white"
        android:text="@string/custom_string"/>
</RelativeLayout>

我最终覆盖Fragment.onResume()并从底层对话框中抓取属性,然后在那里设置宽度/高度参数。我将最外层的布局高度/宽度设置为match_parent。注意,这段代码似乎也尊重我在xml布局中定义的页边距。

@Override
public void onResume() {
    super.onResume();
    ViewGroup.LayoutParams params = getDialog().getWindow().getAttributes();
    params.width = LayoutParams.MATCH_PARENT;
    params.height = LayoutParams.MATCH_PARENT;
    getDialog().getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
}

我修复了它设置根元素布局参数。

int width = activity.getResources().getDisplayMetrics().widthPixels;
int height = activity.getResources().getDisplayMetrics().heightPixels;
content.setLayoutParams(new LinearLayout.LayoutParams(width, height));

更新2021

对于Kotlin用户,我设计了几个简单的扩展方法,可以将DialogFragment的宽度设置为屏幕宽度的百分比,或者接近全屏:

/**
 * 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)
}

然后在你的DialogFragment中或onActivityCreated之后:

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)
    setWidthPercent(85)
}

为子孙后代考虑一下这个答案的其余部分。

问题13:对话片段布局

这真的有点让人麻木。

当创建一个DialogFragment时,你可以选择覆盖onCreateView(它传递一个ViewGroup来附加你的.xml布局)或onCreateDialog,后者没有。

但是你不能同时覆盖这两个方法,因为你很可能会让Android搞不清楚你的对话框布局是否被夸大了!WTF ?

是否覆盖OnCreateView或OnCreateDialog的选择取决于你打算如何使用对话框。

如果你打算允许DialogFragment来控制它自己内部对话框的呈现,那么你需要重写OnCreateView。 如果你打算手动控制DialogFragment的对话框将如何呈现,你需要重写OnCreateDialog。

这可能是世界上最糟糕的事情了。

onCreateDialog疯狂

因此,您正在重写DialogFragment中的onCreateDialog以创建一个自定义的AlertDialog实例以显示在窗口中。酷。但是请记住,onCreateDialog没有接收ViewGroup来附加您的自定义.xml布局。没问题,您只需将null传递给充气方法。

让疯狂开始吧。

当你重写onCreateDialog时,Android完全忽略了你膨胀的.xml布局的根节点的几个属性。这包括但不限于:

background_color layout_gravity layout_width layout_height

这几乎是滑稽的,因为你被要求设置每一个.xml布局的layout_width和layout_height或Android Studio将扇你一个漂亮的小红色耻辱徽章。

光是DialogFragment这个词就让我想吐。我可以写一本充满Android陷阱和混乱的小说,但这是最阴险的一个。

为了回归理智,首先,我们声明一个样式来恢复我们期望的background_color和layout_gravity:

<style name="MyAlertDialog" parent="Theme.AppCompat.Dialog">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:layout_gravity">center</item>
</style>

上面的样式继承自对话框的基本主题(在本例中的AppCompat主题中)。

接下来,我们通过编程的方式应用样式来放回Android刚刚丢弃的值,并恢复标准的AlertDialog外观:

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();
    }
}

上面的代码将使您的AlertDialog看起来像一个AlertDialog。也许这就足够了。

但是等等,还有更多!

如果你正在为你的AlertDialog设置一个特定的layout_width或layout_height,当它显示(很可能),然后你猜什么,你还没有完成!

有趣的是,当你意识到如果你试图在你的花哨的新样式中设置一个特定的layout_width或layout_height时,Android也会完全忽略它!:

<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>

要设置一个特定的窗口宽度或高度,你需要使用一个完整的方法来处理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);
}

许多人效仿Android的坏例子,使用WindowManager。LayoutParams到更通用的ViewGroup。LayoutParams,只是为了右转并施放ViewGroup。LayoutParams返回到WindowManager。几行之后的LayoutParams。有效的Java见鬼去吧,这种不必要的强制转换除了使代码更难破译之外,什么也没有提供。

旁注:在Android SDK中有大约20个重复的LayoutParams——这是一个非常糟糕的设计的完美例子。

总之

对于覆盖onCreateDialog的DialogFragments:

要恢复标准AlertDialog的外观和感觉,创建一个设置background_color = transparent和layout_gravity = center的样式,并在onCreateDialog中应用该样式。 要设置特定的layout_width和/或layout_height,可以在onResume中通过LayoutParams编程来实现 为了保持理智,尽量不要去想Android SDK。