对话框是一个小的窗口用以提示用户做出决定或输入额外的信息。对话框不会填满屏幕并且通常被用作模态事件,要求用户做出行动才能继续下去。


对话框设计:关于如何设计你的对话框,包括一些建议,请阅读 Dialogs 设计向导。 Dialog类是对话框的基类,你应该避免直接实例化Dialog。改为使用如下的一个子类: ​​AlertDialog​​一个可以展示 标题,三个按钮,可选择项的列表或自定义布局 的对话框。 ​​DatePickerDialog​​ or ​​TimePickerDialog​​一个使用预先定义好的UI,允许用户选择一个日期或时间 的对话框 避免使用ProgressDialog             安卓包含了一个名为ProgressDialog的对话框,它可以展示一个带有进度条的对话框。然而,如果你需要指出加载状态或不确定的进度信息,你应该遵循 Progress & Activity 的设计指导,并且在你的布局中使用ProgressBar。
这些类为你的对话框定义了样式和结构,但是你应为你的对话框该使用DialogFragment作为容器。DialogFragment类提供你需要创建你的对话框的所有控制并且管理它的显示,替代在Dialog对象中调用方法。 使用DialogFragment管理对话框,来确保正确的处理生命周期事件,如当用户按下返回按钮或旋转屏幕。DialogFragment类允许你重用该对话框的UI在大的UI中作为可嵌入的组件,就像传统的fragment一样(如在大屏幕和小屏幕上显示不同的对话框UI)。 这篇文章接下来的章节描述了如何结合一个​​AlertDialog​​ 对象来使用DialogFragment。如果你想创建一个日期或时间选择器,你应该去阅读Pickers(选择器)。 :因为该DialogFragment类在安卓3.0才加入进来,该文章描述的如何使用DialogFragment类是由 Support Library提供的。在你的应用中加入该库,你可以使用DialogFragment并且各类可在安卓1.6或更高版本上运行的API。如果你的应用支撑的最低版本是API 11或更高,你可以在使用框架版本的DialogFragment,但是要注意在该文件中的关于支持库API的链接。当使用了支持库,确保你import的是android.support.v4.app.DialogFragment 而不是​​android.app.DialogFragment​​.。

创建对话框碎片(Dialog Fragment)

你可以实现一个广义上的对话框设计——包括自定义布局和在Dialogs设计向导中的描述——通过继承DialogFragment并且在onCreateDialog()回调函数中创建一个AlertDialog。 例如,这里有个基本的AlertDialog 通过​​DialogFragment​​:管理的:

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

现在,当你创建了一个该类的引用并且在该对象中调用了show(),该对话框便会显示出来,如图1. 下面的章节描述了更多关于使用AlertDialog.BuilderAPI来创建对话框的方法。 取决于如何组合你的对话框,你可以实现DialogFragment中的各种回调方法,包括所有基于fragment生命周期的方法。

创建警告对话框(Alert Dialog)

AlertDialog类允许你创建和设计各种各样的对话框,并且总有一个对话框是你需要的。如图2所示,alert dialog上有三个区域: 1.标题     这是一个可选项并且仅当内容被详细消息,一个列表或自定义布局占用时使用。如果你希望说明一个简单的消息或问题(例如图1的对话框),则不需要标题。 2. 内容区域     这里可以展示消息,列表或其它自定义不布局 3.操作按钮     在一个对话框中不超过个三个操作按钮 AlertDialog.Builder类提供API来让你创建一个AlertDialog和这些内容,包括自定义布局。 为创建一个​​AlertDialog​​::

​// 1. 通过它的构造器实例化一个 AlertDialog.Builder​​​​AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());​​​​ ​​​​// 2.将设置方法都集合在一起来设置对话框的属性​​​​builder.setMessage(R.string.dialog_message)​​​​ .setTitle(R.string.dialog_title);​​​​ ​​​​// 3. 从create()中获得​​AlertDialog

​AlertDialog dialog = builder.create();​

接下来的话题展示了如何定义不同的属性,通过使用AlertDialog.Builder类。

添加按钮

添加按钮变为图2所示,调用 ​​setPositiveButton()​​​ 和​​setNegativeButton()方法​​ :

​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() methods 方法请求一个按钮的标题(由string资源文件提供)和一个​​DialogInterface.OnClickListener​​ ,该监听器定义了当用户点击该按钮时的操作。这里有三种不同的操作按钮可以添加:   Positive          你可以使用它来执行同样和继续操作("OK'按钮) Negative         你可以使用它来执行取消操作 Neutral         你应该在用户可能不希望继续操作时使用这个,但并不是一定要取消。它显示在postive和negative按钮之间。例如,该操作获取是“之后再提醒我” 这三种操作按钮可以都添加一个在​


​AlertDialog​​.中。也就是说,不能有多个“positive”按钮。

添加列表

在AlertDialogAPI中有三种类型的列表可用:


  • 一个传统的单选列表
  • 一个持续的单选列表(单选按钮)
  • 一个持续的多选列表(复选框)
  • //持续的说明可以保存选项

像图3一样创建衣服单选列表,使用setItems()方法:

​@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) {​​​​ // which 变量包含了被选中项的索引值​​​​ }​​​​ });​​​​ return builder.create();​​​​}​

因为列表是显示在对话框的文本区,对话框无法同时显示一个消息和列表,并且你应该为对话框设置标题,通过setTitle()。为列表指定列表项,调用setItems(),传递一个数组。或者,你可以使用setAdapter()指定一个列表。使用ListAdapter允许你用动态数据(例如数据库中的数据)返回列表。 如果你选择用​​ListAdapter​​,返回你的列表,经常使用一个Loader来异步加载内容。这些在Building Layouts with an Adapter 和 Loaders开发向导中详细的讲解了。

:默认的,触摸一个列表项将会消失该对话框,除非你使用一个持久的选择列表。

添加一个持久的复选/单选列表

为了添加一个持续的复选项(checkbox)或单选项(radio button),分别使用​​setMultiChoiceItems()​​​ 或​​setSingleChoiceItems()​​ 方法。例如,这里展示了如何创建一个像图4一样的复选列表,并且存储了被选中项在​


​ArrayList​​:中:

​@Override​​​​public Dialog onCreateDialog(Bundle savedInstanceState) {​​​​ mSelectedItems = new ArrayList(); //跟踪被选项的地方​​​​ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());​​​​ // 设置对话框标题​​​​ builder.setTitle(R.string.pick_toppings)​​​​ // 指定列表数组, 默认被选中项t (null for none),和选择项目时的监听器​​​​ .setMultiChoiceItems(R.array.toppings, null,​​​​ new DialogInterface.OnMultiChoiceClickListener() {​​​​ @Override​​​​ public void onClick(DialogInterface dialog, int which,​​​​ boolean isChecked) {​​​​ if (isChecked) {​​​​ // 如果用户选中了项目,将它加入被选择项​​​​ mSelectedItems.add(which);​​​​ } else if (mSelectedItems.contains(which)) {​​​​ // 否则,如果项目已经在数组中,移除它​​​​ mSelectedItems.remove(Integer.valueOf(which));​​​​ }​​​​ }​​​​ })​​​​ // Set the action buttons​​​​ .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {​​​​ @Override​​​​ public void onClick(DialogInterface dialog, int id) {​​​​ // 用户点击OK,因此建议​​mSelectedItems 结果存储在某处,或者从打开的对话框中返回它们。

​ // 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(),如果你想保留用户选项。这样,如果再次打开该对话框应该标明用户当前选中的项,然后创建一个带有单选按钮的列表。

创建自定义布局(Custom Layout)

如果你想在对话框中自定义布局,创建一个布局并且在你的AlertDialog.Builder对象中通过调用​​setView()​​ 来添加进去。 默认的,自定义布局填充该对话框窗口,但是你仍可以使用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,第二个参数是布局的父view。之后你可以调用setView()来在该对话框中放置该布局。

​@Override​​​​public Dialog onCreateDialog(Bundle savedInstanceState) {​​​​ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());​​​​ // Get the layout inflater​​​​ LayoutInflater inflater = getActivity().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作为对话框来展示,代替DialogAPI。简单的创建一个activity并且在​​<activity>​​​ 配置元素中设置它的主题为​​Theme.Holo.Dialog​​ :

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

传递事件出去 给对话框持有者

当用户触摸对话框中的一个操作按钮或从列表中选择一个项目时,你的DialogFragment或许自己完成了必要的操作,但是你经常想要传递事件给打开该dialog的activity或fragment。为了实现该这样一个做法,用一个方法为每一个点击事件类型定义一个接口。在持有组件中实现接口,它将会收到该对话框的操作事件。 例如,这里有个​​DialogFragment​​ ,定义了一个接口,通过它传递事件给持有者的activity:

​public class NoticeDialogFragmentextends DialogFragment {​


​ /* 创建该对话框碎片的activity必须实现该接口​​ ,为了收到事件回调 * 每一个需要传递的方法需都需要声明它     */

​ public interface NoticeDialogListener {​​​​ public void onDialogPositiveClick(DialogFragment dialog);​​​​ public void onDialogNegativeClick(DialogFragment dialog);​​​​ }​


​ // 使用该接口的实例来传递操作事件​​​​ NoticeDialogListener mListener;​


​ // 重写Fragment.onAttach()方法来实例化 NoticeDialogListener​​​​ @Override​​​​ public void onAttach(Activity activity) {​​​​ super.onAttach(activity);​​​​ // 验证持有者的activity 实现了回调接口​​​​ try {​​​​ // Instantiate the NoticeDialogListener so we can send events to the host​​​​ mListener = (NoticeDialogListener) activity;​​​​ } catch (ClassCastException e) {​​​​ // The activity doesn't implement the interface, throw exception​​​​ throw new ClassCastException(activity.toString()​​​​ + " must implement NoticeDialogListener");​​​​ }​​​​ }​​​​ ...​​​​}​

持有该对话框的activity用对话框fragment构造器创建一个该对话框的,并且通过NoticeDialogListener 接口接收该对话框的事件:

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


​ public void showNoticeDialog() {​​​​ // 创建一个 dialog fragment 并且展示它​​​​ DialogFragment dialog = new NoticeDialogFragment();​​​​ dialog.show(getSupportFragmentManager(), "NoticeDialogFragment");​​​​ }​​​​ ​​​​ // dialog fragment 通过Fragment.onAttach()回调​​返回一个参数给改activity ,它被用来调用如下方法,这些方法定义在 NoticeDialogFragment.NoticeDialogListener接口

​ @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()回调方法执行——该对话框fragmeny可以使用该接口回调方法来传递点击事件给activity:

​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​​​​ mListener.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​​​​ mListener.onDialogNegativeClick(NoticeDialogFragment.this);​​​​ }​​​​ });​​​​ return builder.create();​​​​ }​​​​}​

 显示对话框

当你想要显示你的对话框时,创建一个你的DialogFragment的并且调用​​show()​​​, 传递一个 ​​FragmentManager​​和该dialog fragment 的标签名。你可以通过调用getSupportFragmentManager()从FragmentActivity 或​


​getFragmentManager()​​ 从Fragment 中获得FragmentManager。例如:

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

第二个参数,“missiles ”,是一个唯一的标签名,系统用来需要时保存和存储fragment 状态。该标签允许你通过fragment 调用​​findFragmentByTag()​​.来获得。

显示一个全屏的对话框或者嵌入的碎片

你或许有一个UI设计,想要将UI划片,在一些情况下显示一个对话框,但是像全屏或在其它中嵌入片段(或许取决于设备是大屏幕或者小屏幕)。​​DialogFragment​​ 类提供你这些易用性,因为它仍可以像一个可嵌入的片段一样。但是,在这种情况下,你不能使用AlertDialog.Builder或其它的Dialog对象来构建对话框。如果你想要该DialogFragment是可嵌入的,你必须在布局中定义你的对话框UI,然后在​


​onCreateView()​​ 中加载该布局。 这里有一个DialogFragment例子,可以作为一个对话框或可嵌入的碎片来显示(使用了命名为purchase_items.xml的布局):

​public class CustomDialogFragment extends DialogFragment {​​​​ /** 系统调用该方法来获得 DialogFragment's 布局, 不关心是否是作为一个对话框显示还是可嵌入的碎片​​ */

​ @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);​​​​ }​


​ /** 系统调用该方法,仅当创建对话框布局时 */​​​​ @Override​​​​ public Dialog onCreateDialog(Bundle savedInstanceState) {​​​​ // 你必须覆盖该方法的唯一原因就是当使用 onCreateView() 来更改任何对话框属性时。例如,对话框默认包含一个标题,但是你的自定义布局或许不需要它。因此你可以移除对话框标题,但是你必须调用父类来获得该对话框。​​​​ Dialog dialog = super.onCreateDialog(savedInstanceState);​​​​ dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);​​​​ return dialog;​​​​ }​​​​}​

这里有一段决定了是作为对话框显示的fragment 或者全屏UI,基于屏幕大小:

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


​ if (mIsLargeLayout) {​​​​ // 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();​​​​ }​​​​}​

更多关于fragment转换的信息,请查看Fragments 开发向导。 在该案例中,mIsLargeLayout 布尔值指定了设备是否使用的是大布局设计(并且在这上面显示对话框为fragment,否则便是全屏)。最好设置该布尔型的方法就是用alternative resource 值为不同大小的屏幕声明一个布尔资源文件( bool resource value )。例如,这里为不同屏幕大小声明了两种布尔资源: 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:

​boolean mIsLargeLayout;​​​​ ​​​​@Override​​​​public void onCreate(Bundle savedInstanceState) {​​​​ super.onCreate(savedInstanceState);​​​​ setContentView(R.layout.activity_main);​​​​ ​​​​ mIsLargeLayout = getResources().getBoolean(R.bool.large_layout);​​​​}​

一个activity作为对话框显示在大屏幕上

当在小屏幕上时,显示一个对话框作为全屏的UI,在大屏幕上,你可以通过显示一个activity 作为一个对话框 来实现相同的效果。选择哪种方法取决于你的应用设计,但是将activity 作为对话框显示是十分实用的,当你的应用准备在小屏幕上设计时,并且你想改进在平板上的体验,通过显示一个短生命周期的activity 来作为对话框。 为了将activity 作为对话框来显示在大屏幕上,在​​<activity>配置元素中​​应用Theme.Holo.DialogWhenLarge主题:

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

更多关于你的activity 主题样式的信息,请查看 Styles and Themes开发向导。

消散(dismissing)你的对话框

当用户点击任何创建按钮通过AlertDialog.Builder创建对话框时,系统将为你消散该对话框。 当用户点击一个对话框列表中的某一项时,系统也会消散该对话框,除非列表使用的是单选按钮(radio button )或复选框(checkbox)。否则,你可以在你的​​DialogFragment​​​.中调用​​dismiss()​​ 来手工的消散你的对话框。万一你需要执行某些操作,当对话框消失时,你可以在你的​


​DialogFragment​​.中实现onDismiss()方法。 你也可以取消(cancel)一个对话框。这是一个特别的事件,表明用户显示的离开了对话框并且没有完成任务。如果用户点击Back 按钮 或点击对话框外的区域 时就会发生此类事件,或者如果你在对话框中显示的调用cancel()(例如根据对话框中的“Cancel ”按钮)。 如上文示例说展示的,你可以通过在你的DialogFragment类中实现onCancel()来回应取消事件。

:在每个事件调用onCancel()之前,系统调用​​onDismiss()​​​ 。然而,如果你调用​​Dialog.dismiss()​​​ 或​​DialogFragment.dismiss(),​​​system  系统调用onDismiss()但不会调用onCancel()。因此当用户点击了你对话框上的posivity按钮来从view上移除该对话框时,通常调用​​dismiss()​​ 。