Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

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

DialogFragment
AlertDialog с одной кнопкой
Заголовок. Метод setTitle()
Нелёгкий выбор - пример с двумя кнопками
Три кнопки
AlertDialog со списком
AlertDialog с переключателями
AlertDialog с флажками
Автоматическое закрытие окна
AlertDialog с рейтингом
AlertDialog с собственной разметкой
Передать данные в активность

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

DialogFragment

Раньше диалоговое окно вызывали напрямую, сейчас рекомендуется использовать в составе DialogFragment.

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

В ранних примерах вам могут встречаться классы диалогов из устаревших пакетов android.app.DialogFragment, android.support.v4.app.DialogFragment, android.support.v7.app.AppCompatDialogFragment.

После появления AndroidX имена пакетов очередной раз изменились, теперь следует импортировать androidx.fragment.app.DialogFragment, androidx.appcompat.app.AlertDialog и другие аналогичные классы. Не забывайте импортировать правильные пакеты при использовании старых проектов.

Для создания диалога следует наследоваться от класса DialogFragment. Создадим новый класс MyDialogFragment:


// Kotlin
package ru.alexanderklimov.dialog

import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {}


//Java package ru.alexanderklimov.dialog; import androidx.fragment.app.DialogFragment; public class MyDialogFragment extends DialogFragment {}

Вызвать диалоговое окно можно через метод show(). Сделаем это по нажатию кнопки.


// Kotlin
button.setOnClickListener {
    val myDialogFragment = MyDialogFragment()
    val manager = supportFragmentManager
    myDialogFragment.show(manager, "myDialog")
}

//Java public void onClick(View view) { FragmentManager manager = getSupportFragmentManager(); MyDialogFragment myDialogFragment = new MyDialogFragment(); myDialogFragment.show(manager, "myDialog"); }

DialogFragment

Скорее всего вы увидите пустой прямоугольник или квадрат. А возможно у вас просто потемнеет экран активности.

Так как это фрагмент, то нам нужно позвать менеджера фрагментов и попросить его показать фрагмент.

Для вызова диалога мы создаём экземпляр класса MyDialogFragment и вызываем метод show(). Метод принимает два параметра: объект класса FragmentManager, получаемый через метод getSupportFragmentManager(), и тег - идентификатор диалога в виде строковой константы, по которому можно идентифицировать диалоговое окно, если их будет много в нашем проекте.

Существует и альтернативный вариант показа окна через транзакцию.


// Kotlin
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
    val myDialogFragment = MyDialogFragment()
    val manager = supportFragmentManager
    //myDialogFragment.show(manager, "dialog")
    val transaction: FragmentTransaction = manager.beginTransaction()
    myDialogFragment.show(transaction, "dialog")
}

// Java public void onClick(View view) { MyDialogFragment myDialogFragment = new MyDialogFragment(); FragmentManager manager = getSupportFragmentManager(); //myDialogFragment.show(manager, "dialog"); FragmentTransaction transaction = manager.beginTransaction(); myDialogFragment.show(transaction, "dialog"); }

Мы получили пустой бесполезный фрагмент. Следует заняться его конструированием. В созданном классе нужно переопределить метод onCreateDialog(). Если используется разметка, то также используется метод onCreateView(), как и у обычных фрагментов. Скорее всего вы не станете заново изобретать велосипед, а будете использовать готовые варианты диалоговых окон.

AlertDialog

Самый распространённый вариант диалогового окна - это AlertDialog. С него и начнём.

Диалоговое окно AlertDialog является расширением класса Dialog, и это наиболее используемое диалоговое окно в практике программиста. Очень часто требуется показать диалог с кнопками Да и Нет, а также Мур и Мяу. В создаваемых диалоговых окнах можно задавать следующие элементы:

  • заголовок
  • текстовое сообщение
  • кнопки: от одной до трёх
  • список
  • флажки
  • переключатели

AlertDialog с одной кнопкой

Начнём с простого примера - покажем на экране диалоговое окно с одной кнопкой.

В класс фрагмента добавляем метод.


// Kotlin
package ru.alexanderklimov.dialog

import android.app.Dialog
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("Важное сообщение!")
                    .setMessage("Покормите кота!")
                    .setIcon(R.drawable.hungrycat)
                    .setPositiveButton("ОК, иду на кухню") {
                        dialog, id ->  dialog.cancel()
                    }
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}

//Java // Показан только метод, без предыдущего кода для фрагмента @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Важное сообщение!") .setMessage("Покормите кота!") .setIcon(R.drawable.hungrycat) .setPositiveButton("ОК, иду на кухню", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // Закрываем диалоговое окно dialog.cancel(); } }); return builder.create(); }

Внешний вид от версии к версии может меняться. В частности, недавно поменяли цвет текста для кнопки.

AlertDialog с одной кнопкой

Сначала мы создаём объект класса AlertDialog.Builder, передав в качестве параметра ссылку на активность. Затем, используя методы класса Builder, задаём для создаваемого диалога заголовок (метод setTitle()), текстовое сообщение в теле диалога (метод setMessage()), значок (метод setIcon()), а также кнопку через метод под странным названием setPositiveButton().

Сама обработка нажатия кнопки внутри диалогового окна задаётся внутри метода setPositiveButton(). В нашем случае мы просто закрываем окно диалога через метод cancel().

Обратите внимание на не совсем обычный способ вызова череды методов цепочкой через точку .setMessage("Покормите кота!").setIcon(R.drawable.hungrycat) и т.д. Такой синтаксис можно часто увидеть в jQuery. При таком способе не нужно использовать точку с запятой в конце каждого метода, вы просто склеиваете все вызовы. Но можете использовать и обычный синтаксис.

Если вы используете Java 8, то студия предложит использовать лямбда-выражение вместо анонимного класса. Решайте сами, в каком стиле писать код.

Заголовок. Метод setTitle()

Немного остановимся на заголовке. Старайтесь использовать небольшие сообщения. Если сообщение будет средней длины, то оно может разместиться на двух строках. Если сообщение будет слишком длинным, то оно обрежется.


// сообщение на две строки
.setTitle("Важное сообщение! Пожалуйста, прочитайте его!")

// длинное сообщение
.setTitle("Важное сообщение! Пожалуйста, прочитайте его! Очень прошу!")
AlertDialog Title

А если очень хочется вывести длинную строку в заголовке? Тогда вместо setTitle() можно вызвать setCustomTitle() и передать ему View, в нашем случае это будет TextView.


val textView = TextView(activity)

with(textView) {
    textView.text = "Важное сообщение! Пожалуйста, прочитайте его! Очень прошу!"
    textView.textSize = 18.0F
    textView.setTypeface(null, Typeface.BOLD)
    textView.gravity = Gravity.CENTER
}

val builder = AlertDialog.Builder(it)
builder
    //.setTitle("Важное сообщение! Пожалуйста, прочитайте его! Очень прошу!")
    .setCustomTitle(textView)
    .setMessage("Покормите кота!")
    .setIcon(R.drawable.hungrycat)
    .setPositiveButton("ОК, иду на кухню") {
            dialog, id ->  dialog.cancel()
    }
builder.create()

При этом перестал выводиться значок. Обидно.

AlertDialog Title

Нелёгкий выбор - пример с двумя кнопками

Нелёгкий выбор

Теперь рассмотрим пример создания диалогового окна с двумя кнопками на основе иллюстрации.


// Kotlin
package ru.alexanderklimov.dialog

import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("Выбор есть всегда")
                .setMessage("Выбери пищу")
                .setIcon(R.drawable.hungrycat)
                .setCancelable(true)
                .setPositiveButton("Вкусная пища") { _, _ ->
                    Toast.makeText(
                        activity,
                        "Вы сделали правильный выбор",
                        Toast.LENGTH_LONG
                    ).show()
                }
                .setNegativeButton(
                    "Здоровая пища"
                ) { _, _ ->
                    Toast.makeText(
                        activity, "Возможно вы правы",
                        Toast.LENGTH_LONG
                    ).show()
                }
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}


// Java package ru.alexanderklimov.dialog; import ... public class MyDialogFragment extends DialogFragment { @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { String title = "Выбор есть всегда"; String message = "Выбери пищу"; String button1String = "Вкусная пища"; String button2String = "Здоровая пища"; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(title); // заголовок builder.setMessage(message); // сообщение builder.setPositiveButton(button1String, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Toast.makeText(getActivity(), "Вы сделали правильный выбор", Toast.LENGTH_LONG).show(); } }); builder.setNegativeButton(button2String, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Toast.makeText(getActivity(), "Возможно вы правы", Toast.LENGTH_LONG) .show(); } }); builder.setCancelable(true); return builder.create(); } }

Внешний вид диалоговых окон в разных версиях Android.

AlertDialog с кнопками

AlertDialog с кнопками

Общая часть кода осталась прежней - объект класса AlertDialog.Builder, методы для настройки окна, а также кнопки диалога и обработку событий на них. В AlertDialog можно добавить только по одной кнопке каждого типа: Positive, Neutral и Negative, т. е. максимально возможное количество кнопок в диалоге — три. На названия кнопок не обращайте внимания, они не несут смысловой нагрузки, а только определяют порядок вывода. Причём в разных версиях Android порядок менялся. Поэтому на старых устройствах кнопка "Да" может быть первой, а на новых - последней. Для каждой кнопки используется один из методов с префиксом set...Button, которые принимают в качестве параметров надпись для кнопки и интерфейс DialogInterface.OnClickListener, определяющий действие при нажатии. Чтобы пользователь не мог закрыть диалог нажатием в любой точке экрана, вызывается метод setCancelable() с значением true.

Три кнопки

Рассмотрим пример с тремя кнопками. Разницы практически нет. Повторяем все предыдущие шаги, для отображения диалогового окна вызывается метод builder.create(). Например, для создания диалога с кнопками Мяу, Гав, Сам дурак! код будет выглядеть приблизительно так:


// Kotlin
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return activity?.let {
        val builder = AlertDialog.Builder(it)
        builder
            .setCancelable(true)
            .setMessage("Выберите правильный ответ")
            .setPositiveButton("Мяу") { dialog, _ ->
                dialog.cancel()
            }
            .setNeutralButton("Гав") { dialog, _ ->
                dialog.cancel()
            }
            .setNegativeButton("Сам дурак") { dialog, _ ->
                dialog.cancel()
            }
        builder.create()
    } ?: throw IllegalStateException("Activity cannot be null")
}

// Java @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage("Выберите правильный ответ") .setCancelable(true) .setPositiveButton("Мяу", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .setNeutralButton("Гав", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }) .setNegativeButton("Сам дурак!", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); return builder.create(); }

AlertDialog в onCreateDialog

AlertDialog со списком

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


// Kotlin
package ru.alexanderklimov.dialog

import android.app.Dialog
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {

    private val catNames = arrayOf("Васька", "Рыжик", "Мурзик")

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("Выберите кота")
                    .setItems(catNames
                    ) { dialog, which ->
                        Toast.makeText(activity, "Выбранный кот: ${catNames[which]}",
                                Toast.LENGTH_SHORT).show()
                    }
            builder.create()
        } ?: throw IllegalStateException("Activity cannot be null")
    }
}

// Java @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final String[] catNamesArray = {"Васька", "Рыжик", "Мурзик"}; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Выберите кота") .setItems(catNamesArray, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Toast.makeText(getActivity(), "Выбранный кот: " + catNamesArray[which], Toast.LENGTH_SHORT).show(); } }); return builder.create(); }

Выбранный элемент содержится в параметре which. При выборе одного из пунктов меню появится всплывающее уведомление, показывающее выбранного кота.

Напоминаю, что внешний вид окна менялся от версии к версии.

AlertDialog со списком

AlertDialog с переключателями

Для создания диалогового окна с переключателями применяется метод setSingleChoiceitems() вместо метода setItems().


// Kotlin
package ru.alexanderklimov.dialog

import android.app.Dialog
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {

    private val catNames = arrayOf("Васька", "Рыжик", "Мурзик")

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("Выберите любимое имя кота")
                    .setSingleChoiceItems(catNames, -1
                    ) { dialog, item ->
                        Toast.makeText(activity, "Любимое имя кота:  ${catNames[item]}",
                                Toast.LENGTH_SHORT).show()
                    }
                    .setPositiveButton("OK"
                    ) { dialog, id ->
                        // User clicked OK, so save the selectedItems results somewhere
                        // or return them to the component that opened the dialog
                    }
                    .setNegativeButton("Отмена") {
                        dialog, id ->
                    }

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

//Java @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final String[] catNamesArray = {"Васька", "Рыжик", "Мурзик"}; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Выберите любимое имя кота") // добавляем переключатели .setSingleChoiceItems(catNamesArray, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int item) { Toast.makeText( getActivity(), "Любимое имя кота: " + catNamesArray[item], Toast.LENGTH_SHORT).show(); } }) .setPositiveButton("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("Отмена", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { } }); return builder.create(); }

Обратите внимание на следующие детали. При выборе переключателя диалоговое окно закрываться не будет. Поэтому необходимо предусмотреть механизм закрытия окна, например, добавить кнопку. Второй момент - в методе setSingleChoiceitems для первого параметра используется массив значений для переключателей, а для второго параметра используется целочисленное значение индекса переключателя, который будет включён по умолчанию при вызове диалогового окна. Если вы хотите, чтобы все переключатели при запуске были в выключенном состоянии, то используйте значение -1.

AlertDialog с переключателями

AlertDialog с переключателями

AlertDialog с флажками

Если вы хотите использовать вместо переключателей флажки Checkbox для множественного выбора, то вам нужен метод setMultiChoiceItems(). Код практически идентичен предыдущему примеру:


// Kotlin
package ru.alexanderklimov.dialog

import android.app.Dialog
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment

class MyDialogFragment : DialogFragment() {

    private val catNames = arrayOf("Васька", "Рыжик", "Мурзик")
    private val checkedItems = booleanArrayOf(false, true, false)

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return activity?.let {
            val builder = AlertDialog.Builder(it)
            builder.setTitle("Выберите котов")
                .setMultiChoiceItems(
                    catNames, checkedItems
                ) { _, which, isChecked ->
                    checkedItems[which] = isChecked
                    // val name = catNames[which] // Get the clicked item
                    // println(name)
                }
                .setPositiveButton(
                    "Готово"
                ) { _, _ ->
                    for (i in catNames.indices) {
                        val checked = checkedItems[i]
                        if (checked) {
                            println(catNames[i])
                        }
                    }
                }
                .setNegativeButton(
                    "Отмена"
                ) { dialog, _ ->
                    dialog.cancel()
                }

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

//Java @Override public Dialog onCreateDialog(Bundle savedInstanceState) { final String[] catNamesArray = {"Васька", "Рыжик", "Мурзик"}; final boolean[] checkedItemsArray = {false, true, false}; AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle("Выберите котов") .setMultiChoiceItems(catNamesArray, checkedItemsArray, new DialogInterface.OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { checkedItemsArray[which] = isChecked; } }) .setPositiveButton("Готово", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { StringBuilder state = new StringBuilder(); for (int i = 0; i < catNamesArray.length; i++) { state.append(catNamesArray[i]); if (checkedItemsArray[i]) state.append(" выбран\n"); else state.append(" не выбран\n"); } Toast.makeText(getActivity(), state.toString(), Toast.LENGTH_LONG) .show(); } }) .setNegativeButton("Отмена", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }); return builder.create(); }

Первый параметр в методе setMultiChoiceItems() — массив значений для списка с флажками, второй параметр — булевый массив состояний флажков списка по умолчанию при вызове диалога. Например, мы хотим, чтобы второй элемент списка был отмечен флажком, а остальные элементы нужно оставить неотмеченными. В этом случае используем массив из булевых значений:


final boolean[] checkedItemsArray = {false, true, false};

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

AlertDialog с флажками AlertDialog с флажками

Автоматическое закрытие окна

В отличие от сообщения Toast, которое закрывается через одну-две секунды, диалоговые окна сами не закрываются, а ждут реакции пользователя. Но если мы воспользуемся таймером, то сможем обойти это ограничение.

Добавим в проект кнопку для вызова самозакрывающего диалогового окна и напишем код для обработчика щелчка кнопки (старый пример без использования фрагмента):


public void onClick(View v) {
    AlertDialog.Builder builder = new AlertDialog.Builder(v.getContext());
    builder.setTitle("Автоматическое закрытие окна");
    builder.setMessage("Через пять секунд это окно закроется автоматически!");
    builder.setCancelable(true);

    final AlertDialog dlg = builder.create();

    dlg.show();

    final Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        public void run() {
            dlg.dismiss(); // when the task active then close the dialog
            timer.cancel(); // also just top the timer thread, otherwise,
                            // you may receive a crash report
        }
    }, 5000); // через 5 секунд (5000 миллисекунд), the task will be active.
}

AlertDialog с рейтингом (проблемный пример)

Однажды я повстречал в сети пример включения компонента RatingBar в диалог. При тестировании обнаружил, что код работает не совсем корректно. На экран всегда выводятся шесть звёздочек, несмотря на установленные настройки. А если развернуть приложение в альбомный режим, то выводятся и семь звёздочек и больше, в зависимости от размеров экрана. Оставил пример на память, если кто-то разберётся в чём тут была проблема, то дайте знать. Это тоже старый пример без использования фрагмента.


public TextView txtView;

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

    txtView = findViewById(R.id.textView1);
}

public void onClick(View v) {
    showRatingDialog();
}

public void showRatingDialog() {
    final AlertDialog.Builder ratingdialog = new AlertDialog.Builder(this);
    final RatingBar rating = new RatingBar(this);
    
    ratingdialog.setIcon(android.R.drawable.btn_star_big_on);
    ratingdialog.setTitle("Голосуем за любимого кота!");
    ratingdialog.setView(rating);
    rating.setMax(5);
    rating.setNumStars(5);
    rating.setStepSize((float) 1.0);

    ratingdialog.setPositiveButton("Готово",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    txtView.setText(String.valueOf(rating.getRating()));
                    dialog.dismiss();
                }
            })

            .setNegativeButton("Отмена",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });

    ratingdialog.create();
    ratingdialog.show();
}

AlertDialog с RatingBar

Решение проблемы

Читатели предложили несколько вариантов решения проблемы. Как следует из документации, компонент RatingBar следует размещать в макете, который имеет свойство wrap_content. Поступим следующим образом. Создадим отдельную разметку с RatingBar, которую присоединим к диалоговому окну. Итак, разметка:

res/layout/ratingdialog.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearlayout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:padding="2dip" >

    <RatingBar
        android:id="@+id/ratingbar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginBottom="10dp"
        android:layout_marginTop="10dp"
        android:clickable="true"
        android:isIndicator="false"
        android:numStars="3"
        android:rating="2"
        android:stepSize="1.0" />

</LinearLayout>

Теперь модифицированный код:


public void showRatingDialog() {
    
    final AlertDialog.Builder ratingdialog = new AlertDialog.Builder(this);
    
    ratingdialog.setIcon(android.R.drawable.btn_star_big_on);
    ratingdialog.setTitle("Голосуем за любимого кота!");

    View linearlayout = getLayoutInflater().inflate(R.layout.ratingdialog, null);
    ratingdialog.setView(linearlayout); 
    
    final RatingBar rating = (RatingBar)linearlayout.findViewById(R.id.ratingbar);

    ratingdialog.setPositiveButton("Готово",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    txtView.setText(String.valueOf(rating.getRating()));
                    dialog.dismiss();
                }
            })

            .setNegativeButton("Отмена",
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.cancel();
                        }
                    });

    ratingdialog.create();
    ratingdialog.show();
}

RatingBar

Обратите внимание, что для доступа к свойствам RatingBar, мы вызываем метод View.findViewById(), а не Activity.findViewById(), как обычно мы привыкли делать в методе onCreate(), когда опускаем название класса.

AlertDialog с собственной разметкой

Если стандартный вид AlertDialog вас не устраивает, то можете придумать свою разметку и подключить её через метод 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.fragment_custom, 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()); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.fragment_custom, null); builder.setView(view); // Остальной код return builder.create(); }

Передать данные в активность

Для обработки щелчков кнопок в диалоговом окне вы пишете код, в котором указываете родительскую активность.


package ru.alexanderklimov.testapplication;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

public class MyDialogFragment extends DialogFragment {
    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setMessage("Вы жертвуете миллион коту")
                .setIcon(R.drawable.ic_launcher_cat)
                .setTitle("Важно! Максимальный перепост")
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        ((MainActivity) getActivity()).okClicked();
                    }
                })
                .setNegativeButton("Отмена", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        ((MainActivity) getActivity()).cancelClicked();
                    }
                });

        return builder.create();
    }
}

В коде каких-то сложностей нет - устанавливаем заголовок, значок, кнопки. При построении диалогового окна указываем родительскую активность и название методов в ней, которые будут отвечать за обработку нажатий кнопок диалога - в нашем случае это методы okClicked() и cancelClicked(). Кстати, имена методов будут подчёркнуты красной линией и среда разработки предложит создать данные методы в классе активности (используйте комбинацию клавиш Alt+Enter).

Возвращаемся в код главной активности и пропишем код для нажатий кнопок диалогового окна:


public void okClicked() {
    Toast.makeText(getApplicationContext(), "Вы выбрали кнопку OK!",
            Toast.LENGTH_LONG).show();
}

public void cancelClicked() {
    Toast.makeText(getApplicationContext(), "Вы выбрали кнопку отмены!",
            Toast.LENGTH_LONG).show();
}

DialogFragment

Пример был написан по принципу - "работает и ладно". На самом деле пример не совсем грамотный, хотя даже в документации он ещё встречается.

Правильный вариант рассматривается во второй части о диалоговых окнах DialogFragment.

Заключение

Вы познакомились с базовыми принципами использования диалоговых окон. В других статьях рассказывается, как использовать продвинутые приёмы для работы с окнами диалога.

Дополнительное чтение

Обсуждение статьи на форуме.

MaterialAlertDialogBuilder

Реклама