對話框
對話框是一個小窗口,提示用戶做出決定或輸入其他信息。對話框不會填滿屏幕,通常用於需要用戶在繼續操作之前執行操作的模態事件。
對話框設計
有關如何設計對話框的信息(包括語言建議),請閱讀“對話框設計指南”。
Dialog類是對話框的基類,但您應該避免直接實例化Dialog。而是使用以下子類之一:
一個對話框,可以顯示標題,最多三個按鈕,可選項列表或自定義佈局。
DatePickerDialog
or TimePickerDialog
具有預定義UI的對話框,允許用戶選擇日期或時間。
避免使用ProgressDialog
Android包含另一個名爲ProgressDialog的對話框類,它顯示帶有進度條的對話框。但是,如果您需要指示加載或不確定進度,則應遵循Progress&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來完成各種對話框設計 - 包括自定義佈局和Dialogs設計指南中描述的那些。
例如,這是一個在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.Title
這是可選的,僅當內容區域被詳細消息,列表或自定義佈局佔用時才應使用。如果您需要聲明一個簡單的消息或問題(例如圖1中的對話框),則不需要標題。
2.Content area
這可以顯示消息,列表或其他自定義佈局。
3.Action buttons
對話框中的操作按鈕不應超過三個。
AlertDialog.Builder類提供的API允許您使用這些類型的內容創建AlertDialog,包括自定義佈局。
要構建AlertDialog:
// 1. Instantiate an AlertDialog.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 the AlertDialog from create()
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()方法需要按鈕的標題(由字符串資源提供)和DialogInterface.OnClickListener,它定義用戶按下按鈕時要執行的操作。
您可以添加三個不同的操作按鈕:
Positive
您應該使用它來接受並繼續操作(“確定”操作)。
Negative
您應該使用它來取消操作。
Neutral
當用戶可能不想繼續操作時,您應該使用此功能,但不一定要取消。它出現在正負按鈕之間。例如,行動可能是“稍後提醒我”。
您只能將每種按鈕類型中的一種添加到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());
// 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();
}
雖然傳統列表和帶單選按鈕的列表都提供“單選”操作,但如果要保留用戶的選擇,則應使用setSingleChoiceItems()。也就是說,如果稍後再次打開對話框應該指示用戶當前的選擇,那麼您將創建一個帶有單選按鈕的列表。
創建自定義佈局
如果要在對話框中使用自定義佈局,請通過調用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()將佈局放在對話框中。
@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();
}
提示:如果需要自定義對話框,則可以將“活動”顯示爲對話框,而不是使用“對話框API”。只需創建一個活動並將其主題設置爲<activity>清單元素中的Theme.Holo.Dialog:
<activity android:theme="@android:style/Theme.Holo.Dialog" >
這樣。活動現在顯示在對話窗口而不是全屏。
將事件傳遞迴Dialog的宿主
當用戶觸摸對話框的某個操作按鈕或從其列表中選擇一個項目時,DialogFragment可能會自行執行必要的操作,但通常您需要將事件傳遞給打開對話框的活動或片段。爲此,請爲每種類型的單擊事件定義一個帶有方法的接口。然後在主機組件中實現該接口,該接口將從對話框接收操作事件。
例如,這是一個DialogFragment,它定義了一個接口,通過該接口將事件傳遞迴主機活動:
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");
}
}
...
}
託管對話框的活動使用對話框片段的構造函數創建對話框的實例,並通過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
...
}
}
由於host活動實現了NoticeDialogListener(由上面顯示的onAttach()回調方法強制執行),因此對話框片段可以使用接口回調方法將click事件傳遞給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設計中,您希望UI在某些情況下顯示爲對話框,但在其他情況下則顯示爲全屏或嵌入式片段(可能取決於設備是大屏幕還是小屏幕)。 DialogFragment類爲您提供了這種靈活性,因爲它仍然可以作爲可嵌入的片段。
但是,在這種情況下,您無法使用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;
}
}
這裏有一些代碼根據屏幕大小決定是將片段顯示爲對話框還是全屏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布爾值指定當前設備是否應使用應用程序的大型佈局設計(因此將此片段顯示爲對話框,而不是全屏)。設置此類布爾值的最佳方法是使用不同屏幕大小的備用資源值聲明bool資源值。例如,以下是兩種不同屏幕尺寸的bool資源版本:
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>
然後,您可以在活動的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);
}
在大屏幕上將對象顯示爲對話框
在小屏幕上,您可以通過在大屏幕上將“活動”顯示爲對話框來完成相同的結果,而不是在小屏幕上將對話框顯示爲全屏UI。您選擇哪種方法取決於您的應用程序設計,但當您的應用程序已經設計爲小屏幕並且您希望通過將短期活動顯示爲對話框來改善平板電腦體驗時,將對象顯示爲對話框通常很有用。
要僅在大屏幕上將活動顯示爲對話框,請將Theme.Holo.DialogWhenLarge主題應用於<activity>清單元素:
<activity android:theme="@android:style/Theme.Holo.DialogWhenLarge" >
有關使用主題設置活動樣式的更多信息,請參閱樣式和主題指南。
解除對話框
當用戶觸摸使用AlertDialog.Builder創建的任何操作按鈕時,系統會爲您解除對話框。
當用戶觸摸對話框列表中的項目時,系統也會關閉對話框,除非列表使用單選按鈕或複選框。否則,您可以通過調用DialogFragment上的dismiss()來手動關閉對話框。
如果您需要在對話框消失時執行某些操作,可以在DialogFragment中實現onDismiss()方法。
您也可以取消對話框。這是一個特殊事件,表示用戶在未完成任務的情況下明確地離開了對話框。如果用戶按下“返回”按鈕,觸摸對話框區域外的屏幕,或者在對話框中顯式調用cancel()(例如響應對話框中的“取消”按鈕),則會發生這種情況。
如上例所示,您可以通過在DialogFragment類中實現onCancel()來響應cancel事件。
注意:系統在調用onCancel()回調的每個事件上調用onDismiss()。但是,如果調用Dialog.dismiss()或DialogFragment.dismiss(),系統將調用onDismiss()但不調用onCancel()。因此,當用戶按下對話框中的“正”按鈕時,通常應調用dismiss(),以便從視圖中刪除對話框。