Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android資訊 >> Android 對話框 Dialog 深度剖析

Android 對話框 Dialog 深度剖析

編輯:Android資訊

對話框

對話框是提示用戶作出決定或輸入額外信息的小窗口。 對話框不會填充屏幕,通常用於需要用戶采取行動才能繼續執行的模式事件。

對話框設計

這裡寫圖片描述

Dialog 類是對話框的基類,但您應該避免直接實例化 Dialog,而是使用下列子類之一:

AlertDialog

此對話框可顯示標題、最多三個按鈕、可選擇項列表或自定義布局。

DatePickerDialog 或 TimePickerDialog

此對話框帶有允許用戶選擇日期或時間的預定義 UI。

避免使用 ProgressDialog

Android 包括另一種名為 ProgressDialog 的對話框類,可顯示具有進度條的對話框。不過,如需指示加載進度或不確定的進度,則應改為遵循進度和 Activity 的設計指南,並在您的布局中使用 ProgressBar。

這些類定義您的對話框的樣式和結構,但您應該將 DialogFragment 用作對話框的容器。DialogFragment 類提供您創建對話框和管理其外觀所需的所有控件,而不是調用 Dialog 對象上的方法。

使用 DialogFragment 管理對話框可確保它能正確處理生命周期事件,如用戶按“返回”按鈕或旋轉屏幕時。 此外,DialogFragment 類還允許您將對話框的 UI 作為嵌入式組件在較大 UI 中重復使用,就像傳統 Fragment 一樣(例如,當您想讓對話框 UI 在大屏幕和小屏幕上具有不同外觀時)。

注:由於 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();
    }
}

這裡寫圖片描述
圖 1. 一個包含消息和兩個操作按鈕的對話框。

現在,當您創建此類的實例並調用該對象上的 show() 時,對話框將如圖 1 所示。

下文將詳細描述如何使用 AlertDialog.Builder API 創建對話框。

根據對話框的復雜程度,您可以在 DialogFragment 中實現各種其他回調方法,包括所有基礎 片段生命周期方法。

構建提醒對話框

您可以通過 AlertDialog 類構建各種對話框設計,並且該類通常是您需要的唯一對話框類。如圖 2 所示,提醒對話框有三個區域:

這裡寫圖片描述

圖 2. 對話框的布局。

標題

這是可選項,只應在內容區域被詳細消息、列表或自定義布局占據時使用。 如需陳述的是一條簡單消息或問題(如圖 1 中的對話框),則不需要標題。

內容區域

它可以顯示消息、列表或其他自定義布局。

操作按鈕

對話框中的操作按鈕不應超過三個。

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,後者用於定義用戶按下該按鈕時執行的操作。

您可以添加三種不同的操作按鈕:

肯定
您應該使用此按鈕來接受並繼續執行操作(“確定”操作)。

否定
您應該使用此按鈕來取消操作。

中性
您應該在用戶可能不想繼續執行操作,但也不一定想要取消操作時使用此按鈕。 它出現在肯定按鈕和否定按鈕之間。 例如,實際操作可能是“稍後提醒我”。

對於每種按鈕類型,您只能為 AlertDialog 添加一個該類型的按鈕。也就是說,您不能添加多個“肯定”按鈕。

這裡寫圖片描述

圖 3. 一個包含標題和列表的對話框。

添加列表

可通過 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,以便內容以異步方式加載。使用適配器構建布局和加載程序指南中對此做了進一步描述。

注:默認情況下,觸摸列表項會清除對話框,除非您使用的是下列其中一種永久性選擇列表。

這裡寫圖片描述

圖 4. 多選項列表。

添加永久性多選列表或單選列表

要想添加多選項(復選框)或單選項(單選按鈕)列表,請分別使用 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();
}

盡管傳統列表和具有單選按鈕的列表都能提供“單選”操作,但如果您想持久保存用戶的選擇,則應使用 {@linkandroid.app.AlertDialog.Builder#setSingleChoiceItems(int,int,DialogInterface.OnClickListener) setSingleChoiceItems()}。也就是說,如果稍後再次打開對話框時系統應指示用戶的當前選擇,那麼您就需要創建一個具有單選按鈕的列表。

創建自定義布局

這裡寫圖片描述

圖 5. 自定義對話框布局。

如果您想讓對話框具有自定義布局,請創建一個布局,然後通過調用 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();
}

提示:如果您想要自定義對話框,可以改用對話框的形式顯示 Activity,而不是使用 Dialog API。 只需創建一個 Activity,並在 清單文件元素中將其主題設置為 Theme.Holo.Dialog:

<activity android:theme="@android:style/Theme.Holo.Dialog" >

就這麼簡單。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");
        }
    }
    ...
}

對話框的宿主 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;
    }
}

以下代碼可根據屏幕尺寸決定將片段顯示為對話框還是全屏 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 布爾值指定當前設備是否應該使用應用的大布局設計(進而將此片段顯示為對話框,而不是全屏顯示)。 設置這種布爾值的最佳方法是聲明一個布爾資源值,其中包含適用於不同屏幕尺寸的備用資源值。 例如,以下兩個版本的布爾資源適用於不同的屏幕尺寸:

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>

然後,您可以在 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()。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved