Android Dev Summit, October 23-24: two days of technical content, directly from the Android team. Sign-up for livestream updates.

对话框

对话框是提示用户做出决定或输入额外信息的小窗口。对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件。

对话框设计

如需了解有关如何设计对话框的信息(包括语言建议),请阅读对话框设计指南。

Dialog 类是对话框的基类,但您应避免直接实例化 Dialog,而是使用下列子类之一:

AlertDialog
此对话框可显示标题、最多三个按钮、可选择项列表或自定义布局。
DatePickerDialogTimePickerDialog
此对话框带有允许用户选择日期或时间的预定义界面。

注意:Android 包含另一种名为 ProgressDialog 的对话框类,该类可显示带有进度条的对话框。不推荐使用此微件,因为它会在显示进度的情况下阻止用户与应用交互。如果需要指示加载进度或不确定的进度,您应遵循进度和 Activity 的设计指南,并在布局中使用 ProgressBar,而非 ProgressDialog

这些类可为您的对话框定义样式和结构,但您应使用 DialogFragment 作为对话框的容器。DialogFragment 类提供创建对话框和管理其外观所需的所有控件,而非调用 Dialog 对象上的方法。

使用 DialogFragment 管理对话框可确保它能正确处理生命周期事件,如用户按“返回”按钮或旋转屏幕时。此外,DialogFragment 类还允许您以嵌入式组件的形式在较大界面中重复使用对话框的界面,类似于传统的 Fragment(例如,当您想让对话框界面在大屏幕和小屏幕上具有不同外观时)。

本指南的后文将描述如何将 DialogFragmentAlertDialog 对象结合使用。如果您想创建日期或时间选取器,应改为阅读选择器指南。

请注意:由于 DialogFragment 类最初是通过 Android 3.0(API 级别 11)添加的,因此本文档描述的是如何使用支持库附带的 DialogFragment 类。通过将该库添加至应用,您可以在运行 Android 1.6 或更高版本系统的设备上使用 DialogFragment 及其他各类 API。如果应用支持的最低版本是 API 级别 11 或更高版本,则可使用 DialogFragment 的框架版本,但请注意,本文中的链接适用于支持库 API。使用支持库时,请确保您导入的是 android.support.v4.app.DialogFragment 类,而不是 android.app.DialogFragment

创建对话框片段

通过扩展 DialogFragment 并在 onCreateDialog() 回调方法中创建 AlertDialog,您可以完成各种对话框设计,包括自定义布局以及对话框设计指南中描述的布局。

例如,以下是在 DialogFragment 内管理的基础 AlertDialog

Kotlin

class FireMissilesDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return activity?.let {
            // Use the Builder class for convenient dialog construction
            val builder = AlertDialog.Builder(it)
            builder.setMessage(R.string.dialog_fire_missiles)
                    .setPositiveButton(R.string.fire,
                            DialogInterface.OnClickListener { dialog, id ->
                                // FIRE ZE MISSILES!
                            })
                    .setNegativeButton(R.string.cancel,
                            DialogInterface.OnClickListener { dialog, id ->
                                // User cancelled the dialog
                            })
            // Create the AlertDialog object and return it
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}

Java

public class FireMissilesDialogFragment extends DialogFragment {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_fire_missiles)
               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // FIRE ZE MISSILES!
                   }
               })
               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // User cancelled the dialog
                   }
               });
        // Create the AlertDialog object and return it
        return builder.create();
    }
}

图 1. 包含一条消息和两个操作按钮的对话框。

现在,当您创建此类的实例并调用该对象上的 show() 时,对话框将如图 1 所示。

下文将详细描述如何使用 AlertDialog.Builder API 创建对话框。

根据对话框的复杂度,您可以在 DialogFragment 中实现各种其他回调方法,包括所有基本的片段生命周期方法

构建提醒对话框

AlertDialog 类允许您构建各种对话框设计,并且该类通常是您需要的唯一对话框类。如图 2 所示,提醒对话框有三个区域:

图 2. 对话框的布局。

  1. 标题

    这是可选项,只应在详细消息、列表或自定义布局占据内容区域时使用。如需陈述的是一条简单消息或问题(如图 1 中的对话框),则不需要标题。

  2. 内容区域

    它可以显示消息、列表或其他自定义布局。

  3. 操作按钮

    对话框中的操作按钮不应超过三个。

AlertDialog.Builder 类提供的 API 允许您创建含有这几种内容(包括自定义布局)的 AlertDialog

如要构建 AlertDialog,请执行以下操作:

Kotlin

// 1. Instantiate an <code><a href="/reference/android/app/AlertDialog.Builder.html">AlertDialog.Builder</a></code> with its constructor
val builder: AlertDialog.Builder? = activity?.let {
    AlertDialog.Builder(it)
}

// 2. Chain together various setter methods to set the dialog characteristics
builder?.setMessage(R.string.dialog_message)
        .setTitle(R.string.dialog_title)

// 3. Get the <code><a href="/reference/android/app/AlertDialog.html">AlertDialog</a></code> from <code><a href="/reference/android/app/AlertDialog.Builder.html#create()">create()</a></code>
val dialog: AlertDialog? = builder?.create()

Java

// 1. Instantiate an <code><a href="/reference/android/app/AlertDialog.Builder.html">AlertDialog.Builder</a></code> with its constructor
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

// 2. Chain together various setter methods to set the dialog characteristics
builder.setMessage(R.string.dialog_message)
       .setTitle(R.string.dialog_title);

// 3. Get the <code><a href="/reference/android/app/AlertDialog.html">AlertDialog</a></code> from <code><a href="/reference/android/app/AlertDialog.Builder.html#create()">create()</a></code>
AlertDialog dialog = builder.create();

以下主题介绍如何使用 AlertDialog.Builder 类定义各种对话框属性。

添加按钮

要想添加如图 2 所示的操作按钮,请调用 setPositiveButton()setNegativeButton() 方法:

Kotlin

val alertDialog: AlertDialog? = activity?.let {
    val builder = AlertDialog.Builder(it)
    builder.apply {
        setPositiveButton(R.string.ok,
                DialogInterface.OnClickListener { dialog, id ->
                    // User clicked OK button
                })
        setNegativeButton(R.string.cancel,
                DialogInterface.OnClickListener { dialog, id ->
                    // User cancelled the dialog
                })
    }
    // Set other dialog properties
    ...

    // Create the AlertDialog
    builder.create()
}

Java

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Add the buttons
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // User clicked OK button
           }
       });
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // User cancelled the dialog
           }
       });
// Set other dialog properties
...

// Create the AlertDialog
AlertDialog dialog = builder.create();

set...Button() 方法需要一个按钮标题(由字符串资源提供)和一个 DialogInterface.OnClickListener,后者用于定义用户按下该按钮时执行的操作。

您可以添加三种不同的操作按钮:

肯定
您应该使用此按钮来接受并继续执行操作(“确定”操作)。
否定
您应该使用此按钮来取消操作。
中立
此按钮应用于用户可能不想继续执行操作,但也未必想要取消操作的情况。它出现在肯定按钮和否定按钮之间。例如,实际操作可能是“稍后提醒我”。

对于每种按钮类型,您只能为 AlertDialog 添加一个该类型的按钮。换言之,您不能添加多个“肯定”按钮。

图 3. 包含标题和列表的对话框。

添加列表

可通过 AlertDialog API 提供三种列表:

  • 传统的单选列表
  • 永久性单选列表(单选按钮)
  • 永久性多选列表(复选框)

如要创建如图 3 所示的单选列表,请使用 setItems() 方法:

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        builder.setTitle(R.string.pick_color)
                .setItems(R.array.colors_array,
                        DialogInterface.OnClickListener { dialog, which ->
                            // The 'which' argument contains the index position
                            // of the selected item
                        })
        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle(R.string.pick_color)
           .setItems(R.array.colors_array, new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int which) {
               // The 'which' argument contains the index position
               // of the selected item
           }
    });
    return builder.create();
}

由于列表出现在对话框的内容区域,因此对话框无法同时显示消息和列表,您应通过 setTitle() 为对话框设置标题。如要为列表指定项目,请调用 setItems(),并传递数组。或者,您可以使用 setAdapter() 指定列表。如此一来,您便可使用 ListAdapter,以动态数据(如来自数据库的数据)支持列表。

如果您选择通过 ListAdapter 支持列表,请务必使用 Loader,以便内容以异步方式加载。使用适配器构建布局加载程序指南中对此做了进一步描述。

请注意:默认情况下,轻触摸列表项会关闭对话框,除非您使用的是以下某一种永久性选择列表。

添加永久性多选列表或永久性单选列表

如要添加多选项(复选框)或单选项(单选按钮)列表,请分别使用 setMultiChoiceItems()setSingleChoiceItems() 方法。

图 4. 多选项列表。

例如,以下示例展示了如何创建如图 4 所示,将选定项保存在 ArrayList 中的多选列表:

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val selectedItems = ArrayList<Int>() // Where we track the selected items
        val builder = AlertDialog.Builder(it)
        // Set the dialog title
        builder.setTitle(R.string.pick_toppings)
                // Specify the list array, the items to be selected by default (null for none),
                // and the listener through which to receive callbacks when items are selected
                .setMultiChoiceItems(R.array.toppings, null,
                        DialogInterface.OnMultiChoiceClickListener { dialog, which, isChecked ->
                            if (isChecked) {
                                // If the user checked the item, add it to the selected items
                                selectedItems.add(which)
                            } else if (selectedItems.contains(which)) {
                                // Else, if the item is already in the array, remove it
                                selectedItems.remove(Integer.valueOf(which))
                            }
                        })
                // Set the action buttons
                .setPositiveButton(R.string.ok,
                        DialogInterface.OnClickListener { dialog, id ->
                            // User clicked OK, so save the selectedItems results somewhere
                            // or return them to the component that opened the dialog
                            ...
                        })
                .setNegativeButton(R.string.cancel,
                        DialogInterface.OnClickListener { dialog, id ->
                            ...
                        })

        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    selectedItems = new ArrayList();  // Where we track the selected items
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Set the dialog title
    builder.setTitle(R.string.pick_toppings)
    // Specify the list array, the items to be selected by default (null for none),
    // and the listener through which to receive callbacks when items are selected
           .setMultiChoiceItems(R.array.toppings, null,
                      new DialogInterface.OnMultiChoiceClickListener() {
               @Override
               public void onClick(DialogInterface dialog, int which,
                       boolean isChecked) {
                   if (isChecked) {
                       // If the user checked the item, add it to the selected items
                       selectedItems.add(which);
                   } else if (selectedItems.contains(which)) {
                       // Else, if the item is already in the array, remove it
                       selectedItems.remove(Integer.valueOf(which));
                   }
               }
           })
    // Set the action buttons
           .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
               @Override
               public void onClick(DialogInterface dialog, int id) {
                   // User clicked OK, so save the selectedItems results somewhere
                   // or return them to the component that opened the dialog
                   ...
               }
           })
           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
               @Override
               public void onClick(DialogInterface dialog, int id) {
                   ...
               }
           });

    return builder.create();
}

尽管传统列表和带有单选按钮的列表均提供“单选”操作,但若想保留用户的选择,则应使用 setSingleChoiceItems()。换言之,若希望在稍后再次打开对话框时显示用户的当前选择,则需创建带有单选按钮的列表。

创建自定义布局

图 5. 自定义对话框布局。

如果您想让对话框拥有自定义布局,请创建布局,然后通过调用 AlertDialog.Builder 对象上的 setView(),将该布局添加至 AlertDialog

默认情况下,自定义布局会填充对话框窗口,但您仍可使用 AlertDialog.Builder 方法来添加按钮和标题。

例如,以下是图 5 中对话框的布局文件:

res/layout/dialog_signin.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <ImageView
        android:src="@drawable/header_logo"
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:scaleType="center"
        android:background="#FFFFBB33"
        android:contentDescription="@string/app_name" />
    <EditText
        android:id="@+id/username"
        android:inputType="textEmailAddress"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginBottom="4dp"
        android:hint="@string/username" />
    <EditText
        android:id="@+id/password"
        android:inputType="textPassword"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="4dp"
        android:layout_marginLeft="4dp"
        android:layout_marginRight="4dp"
        android:layout_marginBottom="16dp"
        android:fontFamily="sans-serif"
        android:hint="@string/password"/>
</LinearLayout>

提示:默认情况下,当您将 EditText 元素设置为使用 "textPassword" 输入类型时,字体系列会设置为固定宽度。因此,您应将其字体系列更改为 "sans-serif",以便两个文本字段均使用匹配的字体样式。

如要扩展 DialogFragment 中的布局,请通过 getLayoutInflater() 获取一个 LayoutInflater 并调用 inflate(),其中第一个参数是布局资源 ID,第二个参数是布局的父视图。然后,您可以调用 setView(),将布局放入对话框中。

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        // Get the layout inflater
        val inflater = requireActivity().layoutInflater;

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        builder.setView(inflater.inflate(R.layout.dialog_signin, null))
                // Add action buttons
                .setPositiveButton(R.string.signin,
                        DialogInterface.OnClickListener { dialog, id ->
                            // sign in the user ...
                        })
                .setNegativeButton(R.string.cancel,
                        DialogInterface.OnClickListener { dialog, id ->
                            getDialog().cancel()
                        })
        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = requireActivity().getLayoutInflater();

    // Inflate and set the layout for the dialog
    // Pass null as the parent view because its going in the dialog layout
    builder.setView(inflater.inflate(R.layout.dialog_signin, null))
    // Add action buttons
           .setPositiveButton(R.string.signin, new DialogInterface.OnClickListener() {
               @Override
               public void onClick(DialogInterface dialog, int id) {
                   // sign in the user ...
               }
           })
           .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
               public void onClick(DialogInterface dialog, int id) {
                   LoginDialogFragment.this.getDialog().cancel();
               }
           });
    return builder.create();
}

提示:如果您想自定义对话框,可以改用对话框的形式显示 Activity,而非使用 Dialog API。您只需创建一个 Activity,并在 <activity> 清单文件元素中将其主题背景设置为 Theme.Holo.Dialog

<activity android:theme="@android:style/Theme.Holo.Dialog" >

就是如此简单。Activity 现在会显示在一个对话框窗口中,而非全屏显示。

将事件传递回对话框的宿主

当用户轻触对话框的某个操作按钮或从列表中选择某一项时,您的 DialogFragment 可能会自行执行必要操作,但通常您想将事件传递给打开该对话框的 Activity 或片段。为此,请为每种点击事件定义带有方法的接口。然后,在从该对话框接收操作事件的宿主组件中实现该接口。

例如,以下 DialogFragment 定义一个接口,通过该接口将事件传回给宿主 Activity:

Kotlin

class NoticeDialogFragment : DialogFragment() {
    // Use this instance of the interface to deliver action events
    internal lateinit var listener: NoticeDialogListener

    /* The activity that creates an instance of this dialog fragment must
     * implement this interface in order to receive event callbacks.
     * Each method passes the DialogFragment in case the host needs to query it. */
    interface NoticeDialogListener {
        fun onDialogPositiveClick(dialog: DialogFragment)
        fun onDialogNegativeClick(dialog: DialogFragment)
    }

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    override fun onAttach(context: Context) {
        super.onAttach(context)
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = context as NoticeDialogListener
        } catch (e: ClassCastException) {
            // The activity doesn't implement the interface, throw exception
            throw ClassCastException((context.toString() +
                    " must implement NoticeDialogListener"))
        }
    }
}

Java

public class NoticeDialogFragment extends DialogFragment {

    /* The activity that creates an instance of this dialog fragment must
     * implement this interface in order to receive event callbacks.
     * Each method passes the DialogFragment in case the host needs to query it. */
    public interface NoticeDialogListener {
        public void onDialogPositiveClick(DialogFragment dialog);
        public void onDialogNegativeClick(DialogFragment dialog);
    }

    // Use this instance of the interface to deliver action events
    NoticeDialogListener listener;

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = (NoticeDialogListener) context;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(activity.toString()
                    + " must implement NoticeDialogListener");
        }
    }
    ...
}

对话框的宿主 Activity 会使用对话框片段的构造函数创建对话框实例,并通过 NoticeDialogListener 接口的实现接收接收对话框的事件:

Kotlin

class MainActivity : FragmentActivity(),
        NoticeDialogFragment.NoticeDialogListener {

    fun showNoticeDialog() {
        // Create an instance of the dialog fragment and show it
        val dialog = NoticeDialogFragment()
        dialog.show(supportFragmentManager, "NoticeDialogFragment")
    }

    // The dialog fragment receives a reference to this Activity through the
    // Fragment.onAttach() callback, which it uses to call the following methods
    // defined by the NoticeDialogFragment.NoticeDialogListener interface
    override fun onDialogPositiveClick(dialog: DialogFragment) {
        // User touched the dialog's positive button
    }

    override fun onDialogNegativeClick(dialog: DialogFragment) {
        // User touched the dialog's negative button
    }
}

Java

public class MainActivity extends FragmentActivity
                          implements NoticeDialogFragment.NoticeDialogListener{
    ...

    public void showNoticeDialog() {
        // Create an instance of the dialog fragment and show it
        DialogFragment dialog = new NoticeDialogFragment();
        dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");
    }

    // The dialog fragment receives a reference to this Activity through the
    // Fragment.onAttach() callback, which it uses to call the following methods
    // defined by the NoticeDialogFragment.NoticeDialogListener interface
    @Override
    public void onDialogPositiveClick(DialogFragment dialog) {
        // User touched the dialog's positive button
        ...
    }

    @Override
    public void onDialogNegativeClick(DialogFragment dialog) {
        // User touched the dialog's negative button
        ...
    }
}

由于宿主 Activity 会实现 NoticeDialogListener(由以上显示的 onAttach() 回调方法强制执行),因此对话框片段可使用接口回调方法向 Activity 传递点击事件:

Kotlin

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return activity?.let {
            // Build the dialog and set up the button click handlers
            val builder = AlertDialog.Builder(it)

            builder.setMessage(R.string.dialog_fire_missiles)
                    .setPositiveButton(R.string.fire,
                            DialogInterface.OnClickListener { dialog, id ->
                                // Send the positive button event back to the host activity
                                listener.onDialogPositiveClick(this)
                            })
                    .setNegativeButton(R.string.cancel,
                            DialogInterface.OnClickListener { dialog, id ->
                                // Send the negative button event back to the host activity
                                listener.onDialogNegativeClick(this)
                            })

            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }

Java

public class NoticeDialogFragment extends DialogFragment {
    ...

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Build the dialog and set up the button click handlers
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage(R.string.dialog_fire_missiles)
               .setPositiveButton(R.string.fire, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // Send the positive button event back to the host activity
                       listener.onDialogPositiveClick(NoticeDialogFragment.this);
                   }
               })
               .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // Send the negative button event back to the host activity
                       listener.onDialogNegativeClick(NoticeDialogFragment.this);
                   }
               });
        return builder.create();
    }
}

显示对话框

如果您想显示对话框,请创建 DialogFragment 实例并调用 show(),以传递对话框片段的 FragmentManager 和标记名称。

您可以从 FragmentActivity 调用 getSupportFragmentManager() 或从 Fragment 调用 getFragmentManager(),从而获取 FragmentManager。例如:

Kotlin

fun confirmFireMissiles() {
    val newFragment = FireMissilesDialogFragment()
    newFragment.show(supportFragmentManager, "missiles")
}

Java

public void confirmFireMissiles() {
    DialogFragment newFragment = new FireMissilesDialogFragment();
    newFragment.show(getSupportFragmentManager(), "missiles");
}

第二个参数 "missiles" 是系统用于保存片段状态并在必要时进行恢复的唯一标记名称。该标记还允许您通过调用 findFragmentByTag() 来获取片段的句柄。

全屏显示对话框或将其显示为嵌入式片段

您可能采用以下界面设计:想让一部分界面在某些情况下显示为对话框,但在其他情况下全屏显示或显示为嵌入式片段(可能取决于设备使用大屏幕还是小屏幕)。DialogFragment 类可提供这种灵活性,因为其仍可充当嵌入式 Fragment

但在此情况下,您不能使用 AlertDialog.Builder 或其他 Dialog 对象来构建对话框。如果您想让 DialogFragment 拥有嵌入能力,则必须在布局中定义对话框的界面,然后在 onCreateView() 回调中加载布局。

以下示例 DialogFragment 可显示为对话框或嵌入式片段(使用名为 purchase_items.xml 的布局):

Kotlin

class CustomDialogFragment : DialogFragment() {

    /** The system calls this to get the DialogFragment's layout, regardless
    of whether it's being displayed as a dialog or an embedded fragment. */
    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        // Inflate the layout to use as dialog or embedded fragment
        return inflater.inflate(R.layout.purchase_items, container, false)
    }

    /** The system calls this only when creating the layout in a dialog. */
    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        // The only reason you might override this method when using onCreateView() is
        // to modify any dialog characteristics. For example, the dialog includes a
        // title by default, but your custom layout might not need it. So here you can
        // remove the dialog title, but you must call the superclass to get the Dialog.
        val dialog = super.onCreateDialog(savedInstanceState)
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
        return dialog
    }
}

Java

public class CustomDialogFragment extends DialogFragment {
    /** The system calls this to get the DialogFragment's layout, regardless
        of whether it's being displayed as a dialog or an embedded fragment. */
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        // Inflate the layout to use as dialog or embedded fragment
        return inflater.inflate(R.layout.purchase_items, container, false);
    }

    /** The system calls this only when creating the layout in a dialog. */
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // The only reason you might override this method when using onCreateView() is
        // to modify any dialog characteristics. For example, the dialog includes a
        // title by default, but your custom layout might not need it. So here you can
        // remove the dialog title, but you must call the superclass to get the Dialog.
        Dialog dialog = super.onCreateDialog(savedInstanceState);
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        return dialog;
    }
}

以下代码可根据屏幕尺寸确定将片段显示为对话框或全屏界面:

Kotlin

fun showDialog() {
    val fragmentManager = supportFragmentManager
    val newFragment = CustomDialogFragment()
    if (isLargeLayout) {
        // The device is using a large layout, so show the fragment as a dialog
        newFragment.show(fragmentManager, "dialog")
    } else {
        // The device is smaller, so show the fragment fullscreen
        val transaction = fragmentManager.beginTransaction()
        // For a little polish, specify a transition animation
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
        // To make it fullscreen, use the 'content' root view as the container
        // for the fragment, which is always the root view for the activity
        transaction
                .add(android.R.id.content, newFragment)
                .addToBackStack(null)
                .commit()
    }
}

Java

public void showDialog() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    CustomDialogFragment newFragment = new CustomDialogFragment();

    if (isLargeLayout) {
        // The device is using a large layout, so show the fragment as a dialog
        newFragment.show(fragmentManager, "dialog");
    } else {
        // The device is smaller, so show the fragment fullscreen
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        // For a little polish, specify a transition animation
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        // To make it fullscreen, use the 'content' root view as the container
        // for the fragment, which is always the root view for the activity
        transaction.add(android.R.id.content, newFragment)
                   .addToBackStack(null).commit();
    }
}

如需了解有关执行片段事务的详细信息,请参阅片段指南。

在本示例中,mIsLargeLayout 布尔值指定当前设备是否应使用应用的大布局设计(进而将此片段显示为对话框,而非全屏显示)。设置这种布尔值的最佳方法是声明一个布尔资源值,其中包含适用于不同屏幕尺寸的备用资源值。例如,以下两个版本的布尔资源适用于不同屏幕尺寸:

res/values/bools.xml

<!-- Default boolean values -->
<resources>
    <bool name="large_layout">false</bool>
</resources>

res/values-large/bools.xml

<!-- Large screen boolean values -->
<resources>
    <bool name="large_layout">true</bool>
</resources>

然后,您可以在 Activity 的 onCreate() 方法的执行期间初始化 mIsLargeLayout 值:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    isLargeLayout = resources.getBoolean(R.bool.large_layout)
}

Java

boolean isLargeLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    isLargeLayout = getResources().getBoolean(R.bool.large_layout);
}

将 Activity 显示为大屏幕上的对话框

相对于在小屏幕上将对话框显示为全屏界面,您可以在大屏幕上将 Activity 显示为对话框,从而达到相同的效果。您选择的方法取决于应用设计,但当应用已经针对小屏幕进行设计,并且您想通过将短生存期 Activity 显示为对话框来改善平板电脑体验时,将 Activity 显示为对话框往往会很有帮助。

如要仅在大屏幕上将 Activity 显示为对话框,请将 Theme.Holo.DialogWhenLarge 主题应用于 <activity> 清单文件元素:

<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >

如需了解有关通过主题设置 Activity 样式的详细信息,请参阅样式和主题指南。

关闭对话框

当用户轻触使用 AlertDialog.Builder 创建的任何操作按钮时,系统会为您关闭对话框。

系统还会在用户轻触某个对话框列表项时关闭对话框,除非该列表使用单选按钮或复选框。否则,您可以通过在 DialogFragment 上调用 dismiss() 来手动关闭对话框。

如需在对话框消失时执行特定操作,您可以在 DialogFragment 中实现 onDismiss() 方法。

您还可取消对话框。此特殊事件表示用户显式离开对话框,且并未完成任务。如果用户按“返回”按钮、轻触对话框区域外的屏幕,或者您在 Dialog 上显式调用 cancel()(例如,为响应对话框中的“取消”按钮),就会发生这种情况。

如上例所示,您可以通过在 DialogFragment 类中实现 onCancel() 来响应取消事件。

请注意:当每个调用 onCancel() 回调的事件发生时,系统会立即调用 onDismiss()。不过,如果您调用 Dialog.dismiss()DialogFragment.dismiss(),系统会调用 onDismiss()而非 onCancel()。因此,当用户在对话框中按“肯定”按钮,从视图中移除对话框时,您通常应调用 dismiss()