對話方塊

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

對話方塊是一個小型視窗,可提示使用者做決定或輸入其他資訊。對話方塊不會填滿螢幕,通常用於強制回應事件,使用者必須先採取行動才能繼續操作。

對話方塊設計

如要瞭解如何設計對話方塊 (包括語言建議),請參閱對話方塊設計指南。

Dialog 類別是對話方塊的基礎類別,但請避免直接將 Dialog 執行個體化。請改用下列其中一個子類別:

AlertDialog
對話方塊可顯示標題、最多 3 個按鈕、可選取項目清單或自訂版面配置。
DatePickerDialogTimePickerDialog
這個對話方塊含有預先定義的 UI,可讓使用者選取日期或時間。

注意:Android 包含了另一個對話方塊類別 ProgressDialog,顯示含有進度列的對話方塊。這個小工具已淘汰,因為其會阻止使用者在顯示進度時與應用程式互動。如果您需要表明載入或不確定進度,請遵循進度與活動的設計指南並在版面配置中使用 ProgressBar,而非使用 ProgressDialog

這些類別會定義對話方塊的樣式和結構,但您應使用 DialogFragment 做為對話方塊的容器。DialogFragment 類別提供建立對話方塊及管理其外觀所需的所有控制項,而不是在 Dialog 物件上呼叫方法。

使用 DialogFragment 管理對話方塊,可確保它能正確處理生命週期事件,例如使用者按下「返回」按鈕或旋轉畫面時DialogFragment 類別也可讓您重複使用對話方塊的 UI,將其當做大型 UI 中的可嵌入元件,就像傳統的 Fragment 一樣 (例如您希望對話方塊 UI 在不同大小的螢幕上以不同方式呈現)。

本指南的以下章節說明如何搭配使用 DialogFragmentAlertDialog 物件。如要建立日期或時間挑選器,請改為參閱挑選器指南。

注意:由於 DialogFragment 類別最初是為 Android 3.0 (API 級別 11) 新增而成,因此本文件說明如何使用 DialogFragment 類別隨附的支援資料庫。將這個程式庫新增至應用程式後,您就可以在搭載 Android 1.6 或以上版本的裝置上使用 DialogFragment 和各種其他 API。如果您的應用程式支援的最低版本是 API 級別 11 以上,則您可以使用 DialogFragment 架構版本,但請注意,本文件中的連結僅適用於支援資料庫 API。使用支援資料庫時,請務必匯入 android.support.v4.app.DialogFragment 類別,而非 android.app.DialogFragment 類別

建立對話方塊片段

您可以利用各式各樣的對話方塊設計,包括自訂版面配置,以及在對話方塊設計指南中提及的內容,即在 onCreateDialog() 回呼方法中擴充 DialogFragment 並建立 AlertDialog

例如,以下是在 DialogFragment 中管理的基本 AlertDialog

Kotlin

class StartGameDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return activity?.let {
            // Use the Builder class for convenient dialog construction
            val builder = AlertDialog.Builder(it)
            builder.setMessage(R.string.dialog_start_game)
                    .setPositiveButton(R.string.start,
                            DialogInterface.OnClickListener { dialog, id ->
                                // START THE GAME!
                            })
                    .setNegativeButton(R.string.cancel,
                            DialogInterface.OnClickListener { dialog, id ->
                                // User cancelled the dialog
                            })
            // Create the AlertDialog object and return it
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}

Java

public class StartGameDialogFragment 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_start_game)
               .setPositiveButton(R.string.start, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // START THE GAME!
                   }
               })
               .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. 標題

    此為選用步驟,只有在內容區域被詳細訊息、清單或自訂版面配置佔用時,才應使用。如果您需要註明簡單的訊息或問題 (例如圖 1 中的對話方塊),則無需提供標題。

  2. 內容區域

    可顯示訊息、清單或其他自訂版面配置。

  3. 動作按鈕

    對話方塊中最多只能使用三個動作按鈕。

AlertDialog.Builder 類別提供的 API 可讓您使用以下類型的內容建立 AlertDialog,包括自訂版面配置。

建立 AlertDialog 的方法如下:

Kotlin

// 1. Instantiate an <code><a href="/reference/android/app/AlertDialog.Builder.html">AlertDialog.Builder</a></code> with its constructor
val builder: AlertDialog.Builder? = activity?.let {
    AlertDialog.Builder(it)
}

// 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 <code><a href="/reference/android/app/AlertDialog.html">AlertDialog</a></code> from <code><a href="/reference/android/app/AlertDialog.Builder.html#create()">create()</a></code>
val dialog: AlertDialog? = builder?.create()

Java

// 1. Instantiate an <code><a href="/reference/android/app/AlertDialog.Builder.html">AlertDialog.Builder</a></code> 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 <code><a href="/reference/android/app/AlertDialog.html">AlertDialog</a></code> from <code><a href="/reference/android/app/AlertDialog.Builder.html#create()">create()</a></code>
AlertDialog dialog = builder.create();

下列主題說明如何使用 AlertDialog.Builder 類別定義各種對話方塊屬性。

新增按鈕

如要新增動作按鈕 (如圖 2 所示),請呼叫 setPositiveButton()setNegativeButton() 方法:

Kotlin

val alertDialog: AlertDialog? = activity?.let {
    val builder = AlertDialog.Builder(it)
    builder.apply {
        setPositiveButton(R.string.ok,
                DialogInterface.OnClickListener { dialog, id ->
                    // User clicked OK button
                })
        setNegativeButton(R.string.cancel,
                DialogInterface.OnClickListener { dialog, id ->
                    // User cancelled the dialog
                })
    }
    // Set other dialog properties
    ...

    // Create the AlertDialog
    builder.create()
}

Java

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() 方法:

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        builder.setTitle(R.string.pick_color)
                .setItems(R.array.colors_array,
                        DialogInterface.OnClickListener { dialog, which ->
                            // The 'which' argument contains the index position
                            // of the selected item
                        })
        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@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. 包含複選項目的清單。

舉例來說,以下說明如何建立複選清單 (如圖 4 中的清單),將所選項目儲存在 ArrayList 中:

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val selectedItems = ArrayList<Int>() // Where we track the selected items
        val builder = AlertDialog.Builder(it)
        // 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,
                        DialogInterface.OnMultiChoiceClickListener { dialog, which, isChecked ->
                            if (isChecked) {
                                // If the user checked the item, add it to the selected items
                                selectedItems.add(which)
                            } else if (selectedItems.contains(which)) {
                                // Else, if the item is already in the array, remove it
                                selectedItems.remove(which)
                            }
                        })
                // Set the action buttons
                .setPositiveButton(R.string.ok,
                        DialogInterface.OnClickListener { dialog, id ->
                            // User clicked OK, so save the selectedItems results somewhere
                            // or return them to the component that opened the dialog
                            ...
                        })
                .setNegativeButton(R.string.cancel,
                        DialogInterface.OnClickListener { dialog, id ->
                            ...
                        })

        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    selectedItems = 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
                       selectedItems.add(which);
                   } else if (selectedItems.contains(which)) {
                       // Else, if the item is already in the array, remove it
                       selectedItems.remove(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 selectedItems 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() 來保留使用者的選擇。也就是說,如果之後再次開啟這個對話方塊,藉此顯示使用者目前選取的選項,然後建立包含圓形按鈕的清單。

建立自訂版面配置

圖 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() 將版面配置放在對話方塊中。

Kotlin

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        // Get the layout inflater
        val inflater = requireActivity().layoutInflater;

        // 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,
                        DialogInterface.OnClickListener { dialog, id ->
                            // sign in the user ...
                        })
                .setNegativeButton(R.string.cancel,
                        DialogInterface.OnClickListener { dialog, id ->
                            getDialog().cancel()
                        })
        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

Java

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    // Get the layout inflater
    LayoutInflater inflater = requireActivity().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" >

大功告成!活動現在會在對話方塊 (而非全螢幕) 中顯示。

將事件傳回對話方塊的主機

當使用者輕觸其中一個動作的按鈕或從清單中選取項目時,DialogFragment 可能會執行必要的動作,但通常您會想將事件傳到活動,或是開啟對話方塊的片段。方法如下:定義每個按一下事件類型的介面。然後在主機元件中實作該介面,以接收對話方塊中的操作事件。

例如,下列 DialogFragment 定義了一個介面,透過該介面將事件傳遞回主機活動:

Kotlin

class NoticeDialogFragment : DialogFragment() {
    // Use this instance of the interface to deliver action events
    internal lateinit var listener: NoticeDialogListener

    /* 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. */
    interface NoticeDialogListener {
        fun onDialogPositiveClick(dialog: DialogFragment)
        fun onDialogNegativeClick(dialog: DialogFragment)
    }

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    override fun onAttach(context: Context) {
        super.onAttach(context)
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = context as NoticeDialogListener
        } catch (e: ClassCastException) {
            // The activity doesn't implement the interface, throw exception
            throw ClassCastException((context.toString() +
                    " must implement NoticeDialogListener"))
        }
    }
}

Java

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 listener;

    // Override the Fragment.onAttach() method to instantiate the NoticeDialogListener
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        // Verify that the host activity implements the callback interface
        try {
            // Instantiate the NoticeDialogListener so we can send events to the host
            listener = (NoticeDialogListener) context;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            throw new ClassCastException(activity.toString()
                    + " must implement NoticeDialogListener");
        }
    }
    ...
}

代管對話方塊的活動使用對話方塊片段的建構函式建立對話方塊的執行個體,並透過實作 NoticeDialogListener 介面接收對話方塊的事件:

Kotlin

class MainActivity : FragmentActivity(),
        NoticeDialogFragment.NoticeDialogListener {

    fun showNoticeDialog() {
        // Create an instance of the dialog fragment and show it
        val dialog = NoticeDialogFragment()
        dialog.show(supportFragmentManager, "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 fun onDialogPositiveClick(dialog: DialogFragment) {
        // User touched the dialog's positive button
    }

    override fun onDialogNegativeClick(dialog: DialogFragment) {
        // User touched the dialog's negative button
    }
}

Java

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
        ...
    }
}

主機活動實作了 NoticeDialogListener (由上方 onAttach() 回呼方法強制執行),因此對話方塊片段可以使用介面回呼方法,將按一下事件傳送至活動:

Kotlin

    override fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        return activity?.let {
            // Build the dialog and set up the button click handlers
            val builder = AlertDialog.Builder(it)

            builder.setMessage(R.string.dialog_start_game)
                    .setPositiveButton(R.string.start,
                            DialogInterface.OnClickListener { dialog, id ->
                                // Send the positive button event back to the host activity
                                listener.onDialogPositiveClick(this)
                            })
                    .setNegativeButton(R.string.cancel,
                            DialogInterface.OnClickListener { dialog, id ->
                                // Send the negative button event back to the host activity
                                listener.onDialogNegativeClick(this)
                            })

            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }

Java

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_start_game)
               .setPositiveButton(R.string.start, new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       // Send the positive button event back to the host activity
                       listener.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
                       listener.onDialogNegativeClick(NoticeDialogFragment.this);
                   }
               });
        return builder.create();
    }
}

顯示對話方塊

如要顯示對話方塊,請建立 DialogFragment 的執行個體並呼叫 show(),接著為對話方塊片段傳送 FragmentManager 和標記名稱。

您可以透過從 FragmentActivity 呼叫 getSupportFragmentManager(),或從 Fragment 呼叫 getFragmentManager() 來獲取 FragmentManager。例如:

Kotlin

fun confirmStartGame() {
    val newFragment = StartGameDialogFragment()
    newFragment.show(supportFragmentManager, "game")
}

Java

public void confirmStartGame() {
    DialogFragment newFragment = new StartGameDialogFragment();
    newFragment.show(getSupportFragmentManager(), "game");
}

第二個引數 "game" 是不重複的標記名稱,是系統用來在需要時儲存並還原片段狀態。標記也可讓您呼叫 findFragmentByTag() 以取得片段的控制代碼。

顯示對話方塊全螢幕或做為嵌入片段

您可能有一個 UI 設計,其中您希望 UI 的一部分在某些情況下顯示為對話方塊,而在其他情況下則以全螢幕或嵌入片段的形式顯示 (取決於裝置是大螢幕或小螢幕)。DialogFragment 類別提供的彈性,因為其仍可做為嵌入的 Fragment 使用。

但在這種情況下,您無法使用 AlertDialog.Builder 或其他 Dialog 物件來建構對話方塊。如果您希望嵌入 DialogFragment,必須在版面配置中定義對話方塊的 UI,然後在 onCreateView() 回呼中載入版面配置。

以下 DialogFragment 範例可能會顯示為對話方塊或可嵌入的片段 (使用名為 purchase_items.xml 的版面配置):

Kotlin

class CustomDialogFragment : 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 fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        // 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 fun onCreateDialog(savedInstanceState: Bundle): Dialog {
        // 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.
        val dialog = super.onCreateDialog(savedInstanceState)
        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
        return dialog
    }
}

Java

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:

Kotlin

fun showDialog() {
    val fragmentManager = supportFragmentManager
    val newFragment = CustomDialogFragment()
    if (isLargeLayout) {
        // 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
        val 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()
    }
}

Java

public void showDialog() {
    FragmentManager fragmentManager = getSupportFragmentManager();
    CustomDialogFragment newFragment = new CustomDialogFragment();

    if (isLargeLayout) {
        // 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>

接著,您可以在活動的 onCreate() 方法中初始化 mIsLargeLayout 值:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    isLargeLayout = resources.getBoolean(R.bool.large_layout)
}

Java

boolean isLargeLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    isLargeLayout = getResources().getBoolean(R.bool.large_layout);
}

在大螢幕上顯示活動對話方塊

在小螢幕上顯示對話方塊時,您不必將全螢幕對話方塊顯示為對話方塊,只要將 Activity 顯示為對話方塊,就可以達到相同的結果。您可以根據應用程式設計,選擇相應的做法,但如果您的應用程式是針對小型螢幕所設計,且您希望透過將短期活動顯示為對話方塊來改善平板電腦上的體驗時,將活動顯示為對話方塊通常很實用。

如果只想在大螢幕上顯示對話方塊,請將 Theme.Holo.DialogWhenLarge 主題套用至 <activity> 資訊清單元素:

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

如要進一步瞭解如何透過主題設定活動樣式,請參閱樣式和主題指南。

關閉對話方塊

當使用者輕觸任何使用 AlertDialog.Builder 建立的動作按鈕時,系統會為您關閉對話方塊。

使用者輕觸對話方塊清單中的項目時,系統也會關閉對話方塊,除非該清單使用圓形按鈕或核取方塊。或者,您可以在 DialogFragment 上呼叫 dismiss() 來手動關閉對話方塊。

如果您需要在對話方塊關閉時執行特定動作,可以在 DialogFragment 中實作 onDismiss() 方法。

你也可以「Cancel」對話方塊。這個特殊事件會明確指出使用者在未完成工作時已離開對話方塊。如果使用者按了「Back」按鈕、輕觸螢幕以外的區域,或者您明確呼叫了 Dialogcancel() (例如回覆對話方塊中的「Cancel」按鈕)

如以上範例所示,您可以在 DialogFragment 類別中實作 onCancel(),藉此回應取消事件。

注意:每次叫用 onCancel() 回呼時,系統都會呼叫 onDismiss()。不過,如果您呼叫 Dialog.dismiss()DialogFragment.dismiss(),系統會呼叫 onDismiss()但不會呼叫onCancel()。因此,一般而言,當使用者按下對話方塊中的「正面」按鈕時,建議您呼叫 dismiss(),以便將對話方塊從檢視畫面中移除