lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Диалоговые окна

Диалоговое окно — это небольшое окно, которое предлагает пользователю принять решение или ввести дополнительную информацию. Диалоговое окно не заполняет весь экран и, как правило, используется при модальных событиях, для продолжения которых требуется действие пользователя.

Дизайн диалогового окна

Подробную информацию о дизайне диалоговых окон, включая рекомендации для вашего языка см. в Руководстве по дизайну диалоговых окон.

Класс Dialog — это базовый класс для создания диалоговых окон, но реализовывать напрямую класс Dialog не рекомендуется. Вместо этого следует использовать один из следующих подклассов:

AlertDialog
Диалоговое окно, в котором могут отображаться заголовок, кнопки вплоть до трех штук, список из выбираемых элементов либо пользовательский макет.
DatePickerDialog или TimePickerDialog
Диалоговое окно с предопределенным пользовательским интерфейсом, с помощью которого пользователь указывает значения даты или времени.

Эти классы определяют стиль и структуру вашего диалогового окна, однако следует использоватьDialogFragment в качестве контейнера вашего диалогового окна. Класс DialogFragment предоставляет все функции, необходимые для создания диалогового окна и управления его внешним видом, вместо вызова методов к объектуDialog.

Использование DialogFragment для управления диалоговым окном обеспечивает корректную обработку событий жизненного цикла , таких как нажатие пользователем кнопки Назад или поворот экрана. С помощью класса DialogFragment также происходит повторное использование пользовательского интерфейса диалогового окна в качестве встраиваемого компонента в пользовательский интерфейс более высокого уровня — подобно традиционному классу Fragment (например, когда необходимо различное отображение пользовательского интерфеса диалогового окна на больших и маленьких экранах).

В следующих разделах руководства описано использование DialogFragment в сочетании с объектом AlertDialog . Если необходимо создать элемент выбора даты или времени, обратитесь к руководству Элементы выбора.

Примечание: Поскольку класс DialogFragment изначально включен в Android 3.0 (уровень API 11), в настоящем документе описывается использование класса DialogFragment, предоставляемого в Библиотеке поддержки. После добавления этой библиотеки в приложение появится возможность использоватьDialogFragment и множество других API на устройствах, работающих на Android 1.6 и выше. Если минимальная версия вашего приложения поддерживает уровень API 11 и выше, можно использовать фреймворк-версию DialogFragment, но следует иметь в виду, что данный документ ссылается на API со вспомогательными библиотеками. При использовании вспомогательной библиотеки необходимо импортировать класс android.support.v4.app.DialogFragment , а не android.app.DialogFragment.

Создание фрагмента диалогового окна

Вы можете реализовать широкие возможности дизайна диалоговых окон, включая создание пользовательских макетов, а также пути, описанные в руководстве по дизайну Диалоговых окон —путем расширения DialogFragment и созданияAlertDialog в методе обратного вызова onCreateDialog().

Например, имеется базовый класс AlertDialog, управляемый в рамках DialogFragment:

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.

В следующем разделе предоставлена более подробная информация об использовании API AlertDialog.Builder для создания диалоговых окон.

В зависимости от сложности создаваемого диалогового окна возможно реализовать множество других методов обратного вызова в DialogFragment, включая все базовые методы жизненных циклов фрагментов.

Создание диалогового окна оповещения

С помощью класса AlertDialog создается многообразие решений касательно внешнего вида диалогового окна , и зачастую этого класса вполне достаточно. Как показано на рис. 2, диалоговое окно состоит из трех областей:

Рисунок 2. Макет диалогового окна.

  1. Заголовок

    Это дополнительная возможность, которая используется только в случае, если область содержимого занята подробным сообщением, списком или пользовательским макетом. Если необходимо отобразить простое сообщение или вопрос (как, например, в диалоге на рисунке 1), заголовок не нужен.

  2. Область содержимого

    Здесь может отображаться сообщение, список или другой пользовательский макет.

  3. Кнопки действия

    В диалоговом окне не должно содержаться более трех кнопок действия.

Класс 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, который определяет действие, следующее за нажатием кнопки пользователем.

Реализована возможность добавлять три различных вида кнопок действий:

Положительные
Используются для подтверждения и продолжения дейстия (кнопка «ОК»).
Отрицательные
Используются для отмены действия.
Нейтральные
Используются в случаях, когда пользователь может не желать продолжить действие, но при этом необязательно хочет его отменить. Появляется между положительными и отрицательнымиI кнопками. Примером такого действия может быть «Напомнить позже».

Можно добавлять только одну кнопку каждого вида в AlertDialog. Это означает, что нельзя использовать более одной «положительной» кнопки.

Рисунок 3. Диалоговое окно с заголовком и списком.

Добавление списка

В API AlertDialog реализована возможность использования трех видов списков:

  • Традиционный список с выбором одного варианта
  • Интерактивный список с выбором одного варианта (переключатели)
  • Интерактивный список с выбором нескольких вариантов (флажки)

Для создания списка с выбором одного варианта, как на рисунке 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();
}

Несмотря на то, что и традиционный список, и список с переключателями предполагают действие по выбору одного элемента, вам необходимо использовать setSingleChoiceItems(), чтобы сохранить выбор пользователя. Это значит, что при повторном открытии диалогового окна будет отображаться текущий выбор пользователя, а затем создается список с переключателями.

Создание пользовательского макета

Рисунок 5. Пользовательский макет диалогового окна.

Если в диалоговом окне необходим пользовательский макет, нужно создать макет и добавить его в AlertDialog путем вызова setView() в объекте AlertDialog.Builder.

По умолчанию пользовательский мает заполняет окно диалога, при это все равно можно использовать методы 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 вам понадобится LayoutInflater с getLayoutInflater() и вызов 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 в качестве диалога вместо API Dialog. Нужно создать операцию и установить тему Theme.Holo.Dialog в элементе манифеста&lt;операция&gt;:

<activity android:theme="@android:style/Theme.Holo.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
        ...
    }
}

Поскольку выполняемая операция реализуется через NoticeDialogListener с помощью метода обратного вызова onAttach(), во фрагменте диалога могут использоваться методы обратного вызова интерфейса для доставки событий нажатий к операциям:

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 и наименование тега для фрагмента диалога.

Можно получить FragmentManager путем вызова getSupportFragmentManager() из FragmentActivity или getFragmentManager() из Fragment. Пример:

public void confirmFireMissiles() {
    DialogFragment newFragment = new FireMissilesDialogFragment();
    newFragment.show(getSupportFragmentManager(), "missiles");
}

Второй аргумент, "missiles", — это уникальное наименование тега, которое система использует для сохранения и восстановления состояния фрагмента, когда это необходимо. С помощью этого тега также можно управлять фрагментом путем вызова findFragmentByTag().

Отображение диалогового окна в полноэкранном режиме или в виде встроенного фрагмента

Вам может понадобиться макет пользовательского интерфейса, в котором в некоторых ситуациях часть пользовательского интерфейса должна появляться как диалоговое окно , отображаемое в полноэкранном режиме либо в виде встроенного фрагмента (возможно, в зависимости от того, маленький или большой экран у устройства). С помощью класса DialogFragment обеспечивается гибкость решения, поскольку он может вести себя как встраиваемый Fragment.

Тем не менее, в этом случае нельзя использоватьAlertDialog.Builder или другие объекты Dialog для построения диалогового окна. Если необходимо сделать DialogFragment встраиваемым, нужно определить пользовательский интерфейс диалогового окна в макете методом обратного вызова 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;
    }
}

Приведен пример кода, реализующего принятие решения об отображении фргмента в качестве диалогового окна или полноэкранного пользовательского интерфейса на основе размера экрана:

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>

Затем можно инизиализировать значение mIsLargeLayout в течение выполнения метода операции onCreate():

boolean mIsLargeLayout;

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

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

Отображение операции в качестве диалога на больших экранах

Вместо отображения диалогового окна в полноэкранном режиме на экранах малого размера можно отображать Activity в качестве диалогового окна на экранах большого размера. Выбор зависит от дизайна приложения, но отображение операции в качестве диалогового окна имеет смысл, когда приложение предназначено для использования на малых экранах, и необходимо улучшить взаимодейтсвие с ним на планшетах, показывая кратковременные операции в качестве диалогового окна.

Для отображения операции в качестве диалогового окна только на больших экранах необходимо применить тему Theme.Holo.DialogWhenLarge к элементу манифеста &lt;операция&gt;:

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

Подробная информация о темах операций приведена в руководстве Стили и темы.

Закрытие диалогового окна

Когда пользователь нажимает кнопки, созданные с помощью AlertDialog.Builder, система закрывает диалоговое окно самостоятельно.

Система также закрывает диалоговое окно, когда пользователь нажимает на элемент списка в диалоговом окне, за исключением списков с переключателями или флажками. В иных случаях можно вручную закрыть диалоговое окно путем вызова dismiss() в DialogFragment.

В случае, если необходимо произвести определенные действия после закрытия диалогового окна, можно реализовать метод onDismiss() в DialogFragment.

Также можно отменить диалоговое окно. Это особое событие, возникающее, когда пользователь покинул диалоговое окно, не завершив задачу. Так происходит, когда пользователь нажимает кнопку Назад, касается экрана за областью диалогового окна, либо когда задано cancel() в Dialog (например, в качестве отклика на нажатие кнопки «Отмена» в диалоговом окне).

Как показано в примере выше, можно ответить на событие отмены с помощью onCancel() в классе DialogFragment.

Примечание: Система вызывает onDismiss() при каждом событии, которое вызывается методом обратного вызоваonCancel(). Тем не менее, при вызовеDialog.dismiss() или DialogFragment.dismiss(), система вызывает onDismiss() , а не onCancel(). Поэтому в общих случаях вызовdismiss() производится при нажатии пользователем положительной кнопки в диалоговом окне, а после диалоговое окно закрывается.