對話框是提示用戶作出決定或輸入額外信息的小窗口。 對話框不會填充屏幕,通常用於需要用戶採取行動才能繼續執行的模式事件。
Dialog
類是對話框的基類,但您應該避免直接實例化 Dialog
,而是使用下列子類之一:
-
AlertDialog
- 此對話框可顯示標題、最多三個按鈕、可選擇項列表或自定義佈局。
DatePickerDialog
或TimePickerDialog
- 此對話框帶有允許用戶選擇日期或時間的預定義 UI。
避免使用 ProgressDialog
Android 包括另一種名爲ProgressDialog
的對話框類,可顯示具有進度條的對話框。不過,如需指示加載進度或不確定的進度,則應改爲遵循進度和
Activity 的設計指南,並在您的佈局中使用 ProgressBar
。
這些類定義您的對話框的樣式和結構,但您應該將 DialogFragment
用作對話框的容器。DialogFragment
類提供您創建對話框和管理其外觀所需的所有控件,而不是調用 Dialog
對象上的方法。
使用 DialogFragment
管理對話框可確保它能正確處理生命週期事件,如用戶按“返回”按鈕或旋轉屏幕時。
此外,DialogFragment
類還允許您將對話框的 UI 作爲嵌入式組件在較大 UI 中重複使用,就像傳統 Fragment
一樣(例如,當您想讓對話框
UI 在大屏幕和小屏幕上具有不同外觀時)。
本指南的後文將描述如何將 DialogFragment
與 AlertDialog
對象結合使用。
如果您想創建一個日期或時間選取器,應改爲閱讀選取器指南。
注:由於 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
:
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.Builder
API 創建對話框。
根據對話框的複雜程度,您可以在 DialogFragment
中實現各種其他回調方法,包括所有基礎片段生命週期方法。
構建提醒對話框
您可以通過 AlertDialog
類構建各種對話框設計,並且該類通常是您需要的唯一對話框類。如圖 2 所示,提醒對話框有三個區域:
- 標題
這是可選項,只應在內容區域被詳細消息、列表或自定義佈局佔據時使用。 如需陳述的是一條簡單消息或問題(如圖 1 中的對話框),則不需要標題。
- 內容區域
它可以顯示消息、列表或其他自定義佈局。
- 操作按鈕
對話框中的操作按鈕不應超過三個。
AlertDialog.Builder
類提供的 API 允許您創建具有這幾種內容(包括自定義佈局)的AlertDialog
。
要想構建 AlertDialog
,請執行以下操作:
// 1. Instantiate anAlertDialog.Builder
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 theAlertDialog
fromcreate()
AlertDialog dialog = builder.create();
以下主題介紹如何使用 AlertDialog.Builder
類定義各種對話框屬性。
添加按鈕
要想添加如圖 2 所示的操作按鈕,請調用 setPositiveButton()
和 setNegativeButton()
方法:
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // 添加按鈕 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 } }); // 設置其他屬性 ... // 創建 AlertDialog AlertDialog dialog = builder.create();
set...Button()
方法需要一個按鈕標題(由字符串資源提供)和一個 DialogInterface.OnClickListener
,後者用於定義用戶按下該按鈕時執行的操作。
您可以添加三種不同的操作按鈕:
- 肯定
- 您應該使用此按鈕來接受並繼續執行操作(“確定”操作)。
- 否定
- 您應該使用此按鈕來取消操作。
- 中性
- 您應該在用戶可能不想繼續執行操作,但也不一定想要取消操作時使用此按鈕。 它出現在肯定按鈕和否定按鈕之間。 例如,實際操作可能是“稍後提醒我”。
對於每種按鈕類型,您只能爲 AlertDialog
添加一個該類型的按鈕。也就是說,您不能添加多個“肯定”按鈕。
添加列表
可通過 AlertDialog
API 提供三種列表:
- 傳統單選列表
- 永久性單選列表(單選按鈕)
- 永久性多選列表(複選框)
要想創建如圖 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) { // The 'which' argument contains the index position // of the selected item } }); return builder.create(); }
由於列表出現在對話框的內容區域,因此對話框無法同時顯示消息和列表,您應該通過 setTitle()
爲對話框設置標題。要想指定列表項,請調用setItems()
來傳遞一個數組。或者,您也可以使用 setAdapter()
指定一個列表。
這樣一來,您就可以使用 ListAdapter
以動態數據(如來自數據庫的數據)支持列表。
如果您選擇通過 ListAdapter
支持列表,請務必使用 Loader
,以便內容以異步方式加載。使用適配器構建佈局和加載程序指南中對此做了進一步描述。
注:默認情況下,觸摸列表項會清除對話框,除非您使用的是下列其中一種永久性選擇列表。
添加永久性多選列表或單選列表
要想添加多選項(複選框)或單選項(單選按鈕)列表,請分別使用 setMultiChoiceItems()
或 setSingleChoiceItems()
方法。
例如,以下示例展示瞭如何創建如圖 4 所示的多選列表,將選定項保存在一個 ArrayList
中:
@Override public Dialog onCreateDialog(Bundle savedInstanceState) { mSelectedItems = new ArrayList(); // Where we track the selected items AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // 設置 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)); } } }) //設置 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(); }
儘管傳統列表和具有單選按鈕的列表都能提供“單選”操作,但如果您想持久保存用戶的選擇,則應使用 setSingleChoiceItems()
。也就是說,如果稍後再次打開對話框時系統應指示用戶的當前選擇,那麼您就需要創建一個具有單選按鈕的列表。
創建自定義佈局
如果您想讓對話框具有自定義佈局,請創建一個佈局,然後通過調用 AlertDialog.Builder
對象上的 setView()
將其添加到 AlertDialog
。
默認情況下,自定義佈局會填充對話框窗口,但您仍然可以使用 AlertDialog.Builder
方法來添加按鈕和標題。
例如,以下是圖 5 中對話框的佈局文件:
<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()
將佈局放入對話框。
@Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); // Get the layout inflater LayoutInflater inflater = getActivity().getLayoutInflater(); //設置佈局 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:
public class NoticeDialogFragment extends DialogFragment { /*設置回調監聽 * 在那個activity使用,就實現此接口 */ public interface NoticeDialogListener { public void onDialogPositiveClick(DialogFragment dialog); public void onDialogNegativeClick(DialogFragment dialog); } NoticeDialogListener mListener; // 重寫onAttach方法,初始化mListener @Override public void onAttach(Activity activity) { super.onAttach(activity); // Verify that the host activity implements the callback interface try { mListener = (NoticeDialogListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement NoticeDialogListener"); } } ... }
對話框的宿主 Activity 會通過對話框片段的構造函數創建一個對話框實例,並通過實現的 NoticeDialogListener
界面接收對話框的事件:
public class MainActivity extends FragmentActivity implements NoticeDialogFragment.NoticeDialogListener{ ... public void showNoticeDialog() { // 創建dialog對象 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) { // 點擊確定按鈕 ... } @Override public void onDialogNegativeClick(DialogFragment dialog) { // 返回按鈕 ... } }
由於宿主 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 { /** 通過onCreateView加載佈局 */ @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; } }
以下代碼可根據屏幕尺寸決定將片段顯示爲對話框還是全屏 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(); } }
如需瞭解有關執行片段事務的詳細信息,請參閱片段指南。
在本示例中,mIsLargeLayout
布爾值指定當前設備是否應該使用應用的大布局設計(進而將此片段顯示爲對話框,而不是全屏顯示)。 設置這種布爾值的最佳方法是聲明一個布爾資源值,其中包含適用於不同屏幕尺寸的備用資源值。
例如,以下兩個版本的布爾資源適用於不同的屏幕尺寸:
<!-- Default boolean values --> <resources> <bool name="large_layout">false</bool> </resources>
<!-- 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 顯示爲對話框,請將 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()
。