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然後建立和定義相應的對話方塊。比如,想象一下一個遊戲使用兩個不同的對話方塊:一個用來指示這個遊戲已經暫停而另一個來指示遊戲結束。首先,為每個對話方塊定義一個整數:
static final int DIALOG_PAUSED_ID = 0;static final int DIALOG_GAMEOVER_ID = 1;
然後,為每一個ID用一個switch case定義這個onCreateDialog(int) 回呼函數:
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):
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() 方法:
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()方法:
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()。
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)回調管理這個對話方塊,如下所示:
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)方法增加進度值。
比如,你的設定可能看起來像這樣:
ProgressDialog progressDialog;progressDialog = new ProgressDialog(mContext);progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);progressDialog.setMessage("Loading...");progressDialog.setCancelable(false);
設定很簡單。大多數建立代碼也用來更新進度。你可能意識到建立另外一個線程來完成這個進度報告的工作是有必要的,進度通過一個對象返回給活動的使用介面執行緒。如果你對如何通過一個Handler使用另外的線程不熟悉,請參見下面的例子:
這個例子使用了另外一個線程來跟蹤進程進度(計數到100)。這個線程在每次進度更新時通過一個控制代碼Handler發回一條訊息Message。主活動然後更新進度對話方塊。
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儲存:
<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>
這個XML在一個LinearLayout裡定義了一個ImageView 和一個TextView.
- 設定上面這個布局作為對話方塊的內容視圖並為這個ImageView和TextView元素定義內容:
Context 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);
執行個體化對話方塊後,用setContentView(int)設定你的自訂布局作為這個對話方塊的內容視圖,以布局資源ID作為參數。現在這個對話方塊有一個已定義的布局,你可以用findViewById(int)方法從布局中抓取視圖對象。
- 就這樣了。你現在可以顯示該對話方塊了。
以基類對話方塊建立的對話方塊必須有一個標題。如果你不調用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)為該對話方塊設定擴充布局。
下面是一個例子,在一個警告對話方塊中建立自訂布局:
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();
在你的自訂布局中使用警告對話方塊可以讓你利用警告對話方塊的內建特性比如管理按鈕,可選列表,一個標題,一個表徵圖等。