您的当前位置:首页正文

Android对话框Dialog深度剖析

来源:要发发知识网

对话框

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


这里写图片描述

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

AlertDialog

创建对话框片段

您可以完成各种对话框设计—包括自定义布局以及对话框设计指南中描述的布局—通过扩展 DialogFragment 并在 onCreateDialog() 回调方法中创建 AlertDialog。
例如,以下是一个在 DialogFragment 内管理的基础 AlertDialog:
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 中的对话框),则不需要标题。
内容区域
它可以显示消息、列表或其他自定义布局。
操作按钮
对话框中的操作按钮不应超过三个。 AlertDialog.Builder 类提供的 API 允许您创建具有这几种内容(包括自定义布局)的 AlertDialog。
要想构建 AlertDialog,请执行以下操作:
// 1. Instantiate an AlertDialog.Builder with its constructorAlertDialog.Builder builder = new AlertDialog.Builder(getActivity());// 2. Chain together various setter methods to set the dialog characteristicsbuilder.setMessage(R.string.dialog_message) .setTitle(R.string.dialog_title);// 3. Get the AlertDialog from create()AlertDialog dialog = builder.create();

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

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

肯定 您应该使用此按钮来接受并继续执行操作(“确定”操作)。 否定 您应该使用此按钮来取消操作。 中性 您应该在用户可能不想继续执行操作,但也不一定想要取消操作时使用此按钮。 它出现在肯定按钮和否定按钮之间。 例如,实际操作可能是“稍后提醒我”。 对于每种按钮类型,您只能为 AlertDialog 添加一个该类型的按钮。也就是说,您不能添加多个“肯定”按钮。 这里写图片描述

图 3. 一个包含标题和列表的对话框。
添加列表
可通过 AlertDialog API 提供三种列表:
传统单选列表 永久性单选列表(单选按钮) 永久性多选列表(复选框) 要想创建如图 3 所示的单选列表,请使用 setItems() 方法:
@Overridepublic 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();}

这里写图片描述 图 4. 多选项列表。
添加永久性多选列表或单选列表 要想添加多选项(复选框)或单选项(单选按钮)列表,请分别使用 setMultiChoiceItems() 或 setSingleChoiceItems() 方法。
例如,以下示例展示了如何创建如图 4 所示的多选列表,将选定项保存在一个 ArrayList 中:
@Overridepublic Dialog onCreateDialog(Bundle savedInstanceState) { mSelectedItems = 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 mSelectedItems.add(which); } else if (mSelectedItems.contains(which)) { // Else, if the item is already in the array, remove it mSelectedItems.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 mSelectedItems 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();}

尽管传统列表和具有单选按钮的列表都能提供“单选”操作,但如果您想持久保存用户的选择,则应使用 {@linkandroid.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) setSingleChoiceItems()}。也就是说,如果稍后再次打开对话框时系统应指示用户的当前选择,那么您就需要创建一个具有单选按钮的列表。

创建自定义布局

这里写图片描述

提示:默认情况下,当您将 EditText 元素设置为使用 “textPassword” 输入类型时,字体系列将设置为固定宽度。因此,您应该将其字体系列更改为 “sans-serif”,以便两个文本字段都使用匹配的字体样式。
要扩展 DialogFragment 中的布局,请通过 getLayoutInflater() 获取一个 LayoutInflater 并调用 inflate(),其中第一个参数是布局资源 ID,第二个参数是布局的父视图。然后,您可以调用 setView() 将布局放入对话框。
@Overridepublic 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,而不是使用 Dialog API。 只需创建一个 Activity,并在 清单文件元素中将其主题设置为 Theme.Holo.Dialog:
<activity android:theme="@android:style/Theme.Holo.Dialog" >
1

1

就这么简单。Activity 现在会显示在一个对话框窗口中,而非全屏显示。 将事件传递回对话框的宿主 当用户触摸对话框的某个操作按钮或从列表中选择某一项时,您的 DialogFragment 可能会自行执行必要的操作,但通常您想将事件传递给打开该对话框的 Activity 或片段。 为此,请定义一个界面,为每种点击事件定义一种方法。然后在从该对话框接收操作事件的宿主组件中实现该界面。
例如,以下 DialogFragment 定义了一个界面,通过该界面将事件传回给宿主 Activity:
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 mListener; // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener @Override public void onAttach(Activity activity) { super.onAttach(activity); // Verify that the host activity implements the callback interface 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"); } } ...}
1

对话框的宿主 Activity 会通过对话框片段的构造函数创建一个对话框实例,并通过实现的 NoticeDialogListener 界面接收对话框的事件:
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 传递点击事件: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 和标记名称。
您可以通过从 FragmentActivity 调用 getSupportFragmentManager() 或从 Fragment 调用 getFragmentManager() 来获取 FragmentManager。例如:
public void confirmFireMissiles() { DialogFragment newFragment = new FireMissilesDialogFragment(); newFragment.show(getSupportFragmentManager(), "missiles");}

第二个参数 “missiles” 是系统用于保存片段状态并在必要时进行恢复的唯一标记名称。 该标记还允许您通过调用 findFragmentByTag() 获取片段的句柄。
全屏显示对话框或将其显示为嵌入式片段
您可能采用以下 UI 设计:您想让一部分 UI 在某些情况下显示为对话框,但在其他情况下全屏显示或显示为嵌入式片段(也许取决于设备使用大屏幕还是小屏幕)。DialogFragment 类便具有这种灵活性,因为它仍然可以充当嵌入式 Fragment。
但在这种情况下,您不能使用 AlertDialog.Builder 或其他 Dialog 对象来构建对话框。如果您想让 DialogFragment 具有嵌入能力,则必须在布局中定义对话框的 UI,然后在 onCreateView() 回调中加载布局。
以下示例 DialogFragment 可以显示为对话框或嵌入式片段(使用名为 purchase_items.xml 的布局):
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; }}

如需了解有关执行片段事务的详细信息,请参阅片段指南。
在本示例中,mIsLargeLayout 布尔值指定当前设备是否应该使用应用的大布局设计(进而将此片段显示为对话框,而不是全屏显示)。 设置这种布尔值的最佳方法是声明一个布尔资源值,其中包含适用于不同屏幕尺寸的备用资源值。 例如,以下两个版本的布尔资源适用于不同的屏幕尺寸:
res/values/bools.xml<resources> <bool name="large_layout">false</bool></resources>res/values-large/bools.xml<resources> <bool name="large_layout">true</bool></resources>

然后,您可以在 Activity 的 onCreate() 方法执行期间初始化 mIsLargeLayout 值:
boolean mIsLargeLayout;@Overridepublic 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 显示为对话框,请将 Theme.Holo.DialogWhenLarge 主题应用于 activity 清单文件元素:
<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
1

1

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

清除对话框

当用户触摸使用 AlertDialog.Builder 创建的任何操作按钮时,系统会为您清除对话框。
系统还会在用户触摸某个对话框列表项时清除对话框,但列表使用单选按钮或复选框时除外。 否则,您可以通过在 DialogFragment 上调用dismiss() 来手动清除对话框。
如需在对话框消失时执行特定操作,则可以在您的 DialogFragment 中实现 onDismiss() 方法。
您还可以取消对话框。这是一个特殊事件,它表示用户显式离开对话框,而不完成任务。 如果用户按“返回”按钮,触摸对话框区域外部的屏幕,或者您在 Dialog 上显式调用 cancel()(例如,为了响应对话框中的“取消”按钮),就会发生这种情况。
如上例所示,您可以通过在您的 DialogFragment 类中实现onCancel() 来响应取消事件。
注:系统会在每个调用 onCancel() 回调的事件发生时立即调用 onDismiss()。不过,如果您调用 Dialog.dismiss() 或 DialogFragment.dismiss(),系统会调用 onDismiss(),而不会调用 onCancel()。因此,当用户在对话框中按“肯定”按钮,从视图中移除对话框时,您通常应该调用 dismiss()。
我的微信二维码如下,欢迎交流讨论


这里写图片描述

欢迎关注《IT面试题汇总》微信订阅号。每天推送经典面试题和面试心得技巧
微信订阅号二维码如下:


这里写图片描述