Dialogs
對話框通常是一個顯示在當前活動前面的小窗口。下面的活動失去焦點而由對話框接受所有的用戶交互。對話框通常被用來當做通知或者運行中的應用程序相關的短暫活動。
Android API支持下面的對話框對象類型:
- 警告對話框AlertDialog
這個對話框管理0,1,2,或3個按鈕,和/或一個可包含復選框和單選按鈕的可選項列表。這個警告對話框能夠組建大多數用戶界面而且是推薦使用的對話框類型。 - 進度對話框ProgressDialog
用來顯示一個進度輪或進度條。因此它是警告對話框的擴展,它也支持按鈕。 - 日期選擇對話框DatePickerDialog
一個允許用戶選擇日期的對話框。 - 時間選擇對話框TimePickerDialog
一個允許用戶選擇時間的對話框。
如果你想定制你自己的對話框,你可以在基礎Dialog
對象或任何上面列舉的子類對話框上進行擴展並定義一個新的布局。
Showing a Dialog
對話框經常作為活動Activity的一部分來創建和顯示。你通常應該從活動的onCreateDialog(int) 回調方法里創建對話框。當你使用這個回調函數時,Android系統會有效的設置這個活動為每個對話框的所有者,從而自動管理每個對話框的狀態並掛靠到活動上。這樣,每個對話框繼承這個活動的特定屬性。比如,當一個對話框打開時,菜單鍵顯示為這個活動定義的選項菜單,音量鍵修改活動使用的音頻流。
注意: 如果你決定在onCreateDialog()方法之外創建一個對話框,它將不會被附着到活動上。不過,你可以通過setOwnerActivity(Activity)把它附着到一個活動上。
當你想要顯示一個對話框時,調用showDialog(int) 方法並傳遞一個唯一標識這個對話框的整數。
當對話框第一次被請求時,Android從你的活動中調用onCreateDialog(int),你應該在這里初始化這個對話框Dialog。這個回調方法被傳以和showDialog(int)相同的ID。當你創建這個對話框后,在方法的最后返回這個對象。
在對話框被顯示之前,Android還調用了可選的回調函數onPrepareDialog(int, Dialog). 如果你想在每一次對話框被打開時改變它的任何屬性,你可以定義這個方法。這個方法在每次打開對話框時被調用,而onCreateDialog(int) 僅在對話框第一次打開時被調用。如果你不定義onPrepareDialog(),那么這個對話框將保持和上次打開時一樣。這個方法也被傳遞以對話框的ID,和在onCreateDialog()中創建的對話框對象。
定義onCreateDialog(int) 和 onPrepareDialog(int, Dialog) 回調函數的最佳方法是使用一個switch 語句來檢查傳遞進來的id 參數。每個case 應該檢查一個唯一的對話框ID然后創建和定義相應的對話框。比如,想象一下一個游戲使用兩個不同的對話框:一個用來指示這個游戲已經暫停而另一個來指示游戲結束。首先,為每個對話框定義一個整數:
1
2
|
static
final
int
DIALOG_PAUSED_ID =
0
;
static
final
int
DIALOG_GAMEOVER_ID =
1
;
|
然后,為每一個ID用一個switch case定義這個onCreateDialog(int) 回調函數:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
protected
Dialog onCreateDialog(
int
id) {
Dialog dialog;
switch
(id) {
case
DIALOG_PAUSED_ID:
// do the work to define the pause Dialog
break
;
case
DIALOG_GAMEOVER_ID:
// do the work to define the game over Dialog
break
;
default
:
dialog =
null
;
}
return
dialog;
}
|
注意: 在這個例子里,case語句沒有具體內容,因為這超出了本章討論范圍。
當是時候顯示其中之一的對話框時,使用對話框ID調用showDialog(int):
1
|
showDialog(DIALOG_PAUSED_ID);
|
Dismissing a Dialog
當你准備關閉對話框時,你可以通過對這個對話框調用dismiss()來消除它。如果需要,你還可以從這個活動中調用dismissDialog(int) 方法,這實際上將為你對這個對話框調用dismiss() 方法.
如果你想使用onCreateDialog(int) 方法來管理你對話框的狀態(就如同在前面的章節討論的那樣),然后每次你的對話框消除的時候,這個對話框對象的狀態將由該活動保留。如果你決定不再需要這個對象或者清除該狀態是重要的,那么你應該調用removeDialog(int)。這將刪除任何內部對象引用而且如果這個對話框正在顯示,它將被消除。
Using dismiss listeners
如果你希望你的應用程序在一個對話框消亡的時候執行一些流程,那么你應該附着一個on-dismiss偵聽器到對話框上。
首先定義DialogInterface.OnDismissListener 接口。這個接口只有一個方法,onDismiss(DialogInterface),將在對話框消亡的時候被調用。然后簡單的傳遞你的OnDismissListener 實現給setOnDismissListener()。
然而, 請注意對話框也可以被“取消”。這是一個表明對話框被用戶顯示取消的特殊情況。這將在用戶按“返回”按鈕時發生,或者這個對話框顯示的調用cancel() (也許通過對話框上的一個“取消”按鈕)。當一個對話框被取消時,這個OnDismissListener 依然會被通知到,但是如果你希望在對話框被顯示取消時被通知到(而不是通常的消除方式),那么你應該通過setOnCancelListener()注冊一個DialogInterface.OnCancelListener 。
Creating an AlertDialog
一個警告對話框是對話框的擴展類。它能夠構建大多數對話框用戶界面並且是推薦使用的對話框類型。你應該在具備如下特性的時候使用它:
- 一個標題
- 一個文本消息
- 1個,2個或3個按鈕
- 一個可選項列表(可選的復選框或單選按鈕)
為了創建一個警告對話框,使用AlertDialog.Builder 子類。通過AlertDialog.Builder(Context)獲取一個構造器然后使用這個類的公共方法來定義警告對話框的所有屬性。當得到構造器后,通過create().方法來獲取警告對話框對象。
下面的題目說明了如何使用AlertDialog.Builder類來定義不同的警告對話框屬性。如果你在onCreateDialog()回調函數中使用下面的代碼,你可以返回結果對話框對象來顯示它。
Adding buttons
為了創建一個如下圖所示的包含並行按鈕的警告對話框,使用set...Button() 方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
AlertDialog.Builder builder =
new
AlertDialog.Builder(
this
);
builder.setMessage(
"Are you sure you want to exit?"
)
.setCancelable(
false
)
.setPositiveButton(
"Yes"
,
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
id) {
MyActivity.
this
.finish();
}
})
.setNegativeButton(
"No"
,
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
|
首先,為這個對話框添加一個消息setMessage(CharSequence)。然后,開始函數鏈並設置該對話框為不能取消not cancelable (因此用戶不能使用返回按鈕關閉這個對話框)。對每個按鈕,使用任一set...Button() 方法,比如setPositiveButton(),該方法接受按鈕名稱以及一個定義用戶選中按鈕后所采取動作的DialogInterface.OnClickListener。
注意: 你僅可以為這個警告對話框添加其中一種按鈕類型。也就是,你不能包含多個“確定”按鈕。這限制了可能的按鈕數目只能是3個:確定(positive),中立(neutral)和否定(negative)。這些名字和你按鈕的實際功能是技術上無關的,但是應該可以幫助你記錄做了什么。
Adding a list
為了創建一個帶有可選項列表的警告對話框,如下邊所示,可使用setItems()方法:
1
2
3
4
5
6
7
8
9
|
final
CharSequence[] items = {
"Red"
,
"Green"
,
"Blue"
};
AlertDialog.Builder builder =
new
AlertDialog.Builder(
this
);
builder.setTitle(
"Pick a color"
);
builder.setItems(items,
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
item) {
Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
|
首先,用setTitle(CharSequence)方法給對話框添加一個標題。然后,添加用setItems()添加一個可選項列表,該列表接受一組顯示的items和一個DialogInterface.OnClickListener 來定義用戶選中按鈕后所采取動作。
Adding checkboxes and radio buttons
要在對話框里創建一個多選項列表(checkboxes)或者單選項(radio buttons),可分別調用setMultiChoiceItems() 和setSingleChoiceItems() 方法。如果你在onCreateDialog()回調函數中創建這些可選列表,Android會幫你管理列表狀態。只要這個活動是激活的,對話框會記住之前選中的items,但如果用戶退出這個活動,用戶選擇將丟失。
注意: 為了在用戶離開或暫停這個活動的時候能夠保存選擇,你必須通過活動生命期Activity Lifecycle來恰當的保存和恢復設置。為了永久保存選項,即使活動進程被完全終止,你需要使用數據存儲Data Storage技術。
要創建如下邊所示的一個包含單選項列表的警告對話框,使用前面例子中相同的代碼,不過需要把setItems()方法替換為setSingleChoiceItems()。
1
2
3
4
5
6
7
8
9
|
final
CharSequence[] items = {
"Red"
,
"Green"
,
"Blue"
};
AlertDialog.Builder builder =
new
AlertDialog.Builder(
this
);
builder.setTitle(
"Pick a color"
);
builder.setSingleChoiceItems(items, -
1
,
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
item) {
Toast.makeText(getApplicationContext(), items[item], Toast.LENGTH_SHORT).show();
}
});
AlertDialog alert = builder.create();
|
setSingleChoiceItems() 的第二個參數是一個checkedItem整型數值,指示了最小選擇值為0的缺省選擇項的位置。“-1”代表不會有默認選擇項。
Creating a ProgressDialog
進度對話框ProgressDialog是AlertDialog類的一個擴展,可以為一個未定義進度的任務顯示一個旋轉輪形狀的進度動畫,或者為一個指定進度的任務顯示一個進度條。這個對話框也能提供按鈕,比如一個取消下載的按鈕。
可以簡單的通過調用ProgressDialog.show()方法來顯示一個進度對話框。比如, 可以很簡單的得到下邊顯示的進度對話框,而不必通過onCreateDialog(int)回調管理這個對話框,如下所示:
1
2
|
ProgressDialog dialog = ProgressDialog.show(MyActivity.
this
,
""
,
"Loading. Please wait..."
,
true
);
|
第一個參數是應用程序上下文Context,第二個是對話框標題(此處為空),第三個是信息,最后這個參數表明進度是否是不確定的(這只和創建進度條有關,下一章會有描述)。
進度對話框的缺省類型是一個旋轉輪,如果你想創建一個間隔進度,需要更多的代碼,如下章所述。
Showing a progress bar
使用動畫進度條顯示進度:
- 用類構造器初始化進度對話框,ProgressDialog(Context)。
- 用setProgressStyle(int)方法設置進度風格為"STYLE_HORIZONTAL"以及設置其它屬性,比如消息。
- 當你准備顯示這個對話框時,調用show()或者從onCreateDialog(int)回調中返回ProgressDialog。
- 你可以通過調用setProgress(int)設置當前進度百分比或者調用incrementProgressBy(int)方法增加進度值。
比如,你的設置可能看起來像這樣:
1
2
3
4
5
|
ProgressDialog progressDialog;
progressDialog =
new
ProgressDialog(mContext);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage(
"Loading..."
);
progressDialog.setCancelable(
false
);
|
設置很簡單。大多數創建代碼也用來更新進度。你可能意識到創建另外一個線程來完成這個進度報告的工作是有必要的,進度通過一個對象返回給活動的用戶界面線程。如果你對如何通過一個Handler使用另外的線程不熟悉,請參見下面的例子:
這個例子使用了另外一個線程來跟蹤進程進度(計數到100)。這個線程在每次進度更新時通過一個句柄Handler發回一條消息Message。主活動然后更新進度對話框。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
|
package
com.example.progressdialog;
import
android.app.Activity;
import
android.app.Dialog;
import
android.app.ProgressDialog;
import
android.os.Bundle;
import
android.os.Handler;
import
android.os.Message;
import
android.view.View;
import
android.view.View.OnClickListener;
import
android.widget.Button;
public
class
NotificationTest
extends
Activity {
static
final
int
PROGRESS_DIALOG =
0
;
Button button;
ProgressThread progressThread;
ProgressDialog progressDialog;
/** Called when the activity is first created. */
public
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Setup the button that starts the progress dialog
button = (Button) findViewById(R.id.progressDialog);
button.setOnClickListener(
new
OnClickListener(){
public
void
onClick(View v) {
showDialog(PROGRESS_DIALOG);
}
});
}
protected
Dialog onCreateDialog(
int
id) {
switch
(id) {
case
PROGRESS_DIALOG:
progressDialog =
new
ProgressDialog(NotificationTest.
this
);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setMessage(
"Loading..."
);
progressThread =
new
ProgressThread(handler);
progressThread.start();
return
progressDialog;
default
:
return
null
;
}
}
// Define the Handler that receives messages from the thread and update the progress
final
Handler handler =
new
Handler() {
public
void
handleMessage(Message msg) {
int
total = msg.getData().getInt(
"total"
);
progressDialog.setProgress(total);
if
(total >=
100
){
dismissDialog(PROGRESS_DIALOG);
progressThread.setState(ProgressThread.STATE_DONE);
}
}
};
/** Nested class that performs progress calculations (counting) */
private
class
ProgressThread
extends
Thread {
Handler mHandler;
final
static
int
STATE_DONE =
0
;
final
static
int
STATE_RUNNING =
1
;
int
mState;
int
total;
ProgressThread(Handler h) {
mHandler = h;
}
public
void
run() {
mState = STATE_RUNNING;
total =
0
;
while
(mState == STATE_RUNNING) {
try
{
Thread.sleep(
100
);
}
catch
(InterruptedException e) {
Log.e(
"ERROR"
,
"Thread Interrupted"
);
}
Message msg = mHandler.obtainMessage();
Bundle b =
new
Bundle();
b.putInt(
"total"
, total);
msg.setData(b);
mHandler.sendMessage(msg);
total++;
}
}
/* sets the current state for the thread,
* used to stop the thread
*/
public
void
setState(
int
state) {
mState = state;
}
}
}
|
Creating a Custom Dialog
如果你想為對話框做一個自定義的設計,你可以為對話框窗口創建自己的布局和部件元素。當你定義好布局后,傳遞根視圖對象或者布局資源ID給setContentView(View) 方法。
比如,創建如下圖所示對話框:
- 創建一個XML布局以custom_dialog.xml保存:
123456789101112131415161718<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:id
=
"@+id/layout_root"
android:orientation
=
"horizontal"
android:layout_width
=
"fill_parent"
android:layout_height
=
"fill_parent"
android:padding
=
"10dp"
>
<
ImageView
android:id
=
"@+id/image"
android:layout_width
=
"wrap_content"
android:layout_height
=
"fill_parent"
android:layout_marginRight
=
"10dp"
/>
<
TextView
android:id
=
"@+id/text"
android:layout_width
=
"wrap_content"
android:layout_height
=
"fill_parent"
android:textColor
=
"#FFF"
/>
</
LinearLayout
>
- 設置上面這個布局作為對話框的內容視圖並為這個ImageView和TextView元素定義內容:
12345678Context mContext = getApplicationContext();
Dialog dialog =
new
Dialog(mContext);
dialog.setContentView(R.layout.custom_dialog);
dialog.setTitle(
"Custom Dialog"
);
TextView text = (TextView) dialog.findViewById(R.id.text);
text.setText(
"Hello, this is a custom dialog!"
);
ImageView image = (ImageView) dialog.findViewById(R.id.image);
image.setImageResource(R.drawable.android);
- 就這樣了。你現在可以顯示該對話框了。
以基類對話框創建的對話框必須有一個標題。如果你不調用setTitle(),那么標題占用的空間保持為空,但仍然可見。如果你根本不想要一個標題,那你應該使用警告對話框AlertDialog來創建你的自定義對話框。 然而,因為警告對話框可以很簡單的通過AlertDialog.Builder 類來創建,你並不需要訪問上面使用的setContentView(int) 方法。相反,你必須使用setView(View)。這個方法接受一個視圖View 對象,所以你需要在XML中擴充布局的根視圖。
要擴充XML布局,用getLayoutInflater() 或getSystemService()方法獲取LayoutInflater,然后調用 inflate(int, ViewGroup),這里第一個參數是布局資源ID而第二個參數是根視圖的ID。在此處,你可以使用擴充布局來查找視圖對象和為ImageView和TextView元素定義內容。然后實例化AlertDialog.Builder 並調用setView(View)為該對話框設置擴充布局。
下面是一個例子,在一個警告對話框中創建自定義布局:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
AlertDialog.Builder builder;
AlertDialog alertDialog;
Context mContext = getApplicationContext();
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.custom_dialog,
(ViewGroup) findViewById(R.id.layout_root));
TextView text = (TextView) layout.findViewById(R.id.text);
text.setText(
"Hello, this is a custom dialog!"
);
ImageView image = (ImageView) layout.findViewById(R.id.image);
image.setImageResource(R.drawable.android);
builder =
new
AlertDialog.Builder(mContext);
builder.setView(layout);
alertDialog = builder.create();
|
在你的自定義布局中使用警告對話框可以讓你利用警告對話框的內置特性比如管理按鈕,可選列表,一個標題,一個圖標等。