Диалоги — это очень удобный и приятный для пользователя способ взаимодействия с приложением. Но при их использовании разработчик может столкнуться с некоторыми трудностями при смене ориентации экрана и иных переменах в жизненном цикле приложения. Давайте рассмотрим как разработчики Android SDK помогли нам с этой проблемой.

Методы и коллбэки Activity для работы с диалогами

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

Имя метода Назначение
showDialog(int, Bundle) Запрос на отображение диалога согласно коду. Если диалог показывается впервые, то далее будет вызван метод onCreateDialog(int, Bundle), а затем onPrepareDialog(int, Bundle). Если диалог показывает второй или более раз (и при это для него ны вызывали метод removeDialog(int)), то будет вызван только onPrepareDialog(int, Dialog, Bundle).
onCreateDialog(int, Bundle) В этом методе создается диалог согласно коду, далее всегда следует вызов метода onPrepareDialog(int, Bundle). Если диалог не удалялся принудительно методом removeDialog(int), то этот метод для одного и тоже диалога будет вызван только один раз за жизнь Activity. Из этого следует, что любую настройку диалога следует проводить в методе onPrepareDialog(int, Dialog, Bundle).
onPrepareDialog(int, Dialog, Bundle) Перед тем как отобразить диалог, система всегда вызывает этот метод. Если вам нужно задавать какие-то значение для диалога, которые могут меняться от показа к показу, то именно тут для этого самое место.
dismissDialog(int) Прячет диалог, но не уничтожает его.
removeDialog(int) Прячет диалог, если он был на экране, и уничтожает его. Если вы уверены, что больше этот диалог вам не понадобиться, то смело его удаляйте.

Заметка: для первых 3 методов существуют аналогичные, но без аргумента Bunlde. В примерах кода мы будем больше использовать именно их, так как наши диалоги достаточно просты и не требуют дополнительной инициализации.

Владея всем этим арсеналом, приступим к делу.

DialogFactory — зачем и как?

Безусловно, мы могли бы создавать нужные нам диалоги прямо в методе onCreateDialog(), но давайте предположим, что нам понадобится такой же набор диалогов для другой Activity. Вместо того, чтобы заниматься копи-пастой, я предлагаю сразу сделать фабричный метод для диалогов. Внимание, код!


package by.idev.dialog;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.EditText;
import android.widget.Toast;
/**
* Утилитарный класс с фабричным методом
* для создания диалогов по коду
*
* @author dmitrykunin
*
*/
public class DialogFactory {
protected DialogFactory() {};


//Констатны, описывающие различные типы диалогов
public final static int DIALOG_ALERT = 0;
public final static int DIALOG_PROGRESS = 1;
public final static int DIALOG_INPUT = 2;


/**
* Фабричный метод возращающий
* диалог по его коду
*
* @param id код диалога
* @param context
* @return диалог, соответсвующий коду
*/
public static Dialog getDialogById(int id, final Context context) {

Dialog dialog = null;
switch (id) {
case DIALOG_ALERT:

dialog = createAlertDialog(context);
break;
case DIALOG_PROGRESS:

dialog = createProgressDialog(context);
break;
case DIALOG_INPUT:

dialog = createInputAlert(context);
break;
}
return dialog;
}

}

Теперь рассмотрим по очереди создание AlertDialog, содержащего ListView (метод createAlertDialog()), ProgressDialog в простейшем виде ( метод createProgressDialog()), и AlertDialog c текстовым полем ввода (метод createInputDialog()).

AlertDialog со списком значений

Для начала проиллюстрирую, что мы получим в итоге.

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


private static Dialog createAlertDialog(final Context context) {
Dialog dialog;
Builder builder = new AlertDialog.Builder(context);

//В этом диалоге будет список доменов,
//по клику на один из которых отработает событие
final String[] domens = {"idev.by", "google.com", "apple.com"};

DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogArg, int which) {
Toast.makeText(context, "Button clicked: "+which, 1000).show();
dialogArg.dismiss();
}
};

DialogInterface.OnClickListener onItemClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogArg, int which) {
Toast.makeText(context, "Item clicked: "+domens[which], 1000).show();
dialogArg.dismiss();
}
};

//Пример работы с Builder, удобная инициализация диалога
dialog = builder.setTitle("Alert dialog")
.setPositiveButton("Positive", onClickListener)
.setNeutralButton("Neutral", onClickListener)
.setNegativeButton("Negative", onClickListener)
.setItems(domens, onItemClickListener).create();
return dialog;
}

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

ProgressDialog

В нашем случае будет иметь вид:


private static Dialog createProgressDialog(final Context context) {
ProgressDialog dialog;
dialog = new ProgressDialog(context);
dialog.setMessage("Please wait");
dialog.setButton(DialogInterface.BUTTON_POSITIVE, "Dismiss", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return dialog;
}

Все довольно просто.

AlertDialog с тектовым полем

Делать нечто подобное приходится довольно часто, например, если нужно спросить что-то у пользователя.

После ввода текста и нажатия "ОК" на экране появится тост с введенным текстом.


private static Dialog createInputAlert(final Context context) {
Dialog dialog;
Builder builder = new Builder(context);

//Создадим текстовое поле для ввода
final EditText editText = new EditText(context);

DialogInterface.OnClickListener onClickListener = new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String input = editText.getText().toString();
Toast.makeText(context, "Input was: "+input, 1000).show();
dialog.dismiss();
}
};

dialog = builder.setTitle("Input Alert")
.setMessage("Please enter something")
.setPositiveButton("Ok", onClickListener)
.setView(editText).create();
return dialog;
}

Ну вот мы наколбасили диалогов, а посмотреть на них не можем. Так что давай-те перейдем к описанию Activity, которая всем покажет.. наши диалоги =)

Работа с диалогами при помощи Activity

Суммарное количество кода тут не велико, за счет помощи со стороны SDK, нам не нужно реализовывать восстановление диалогов при поворотах экрана и заниматься прочими «веселыми» делами.


package by.idev.dialog;
import java.util.Date;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
public class ConstructiveDialogActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}

@Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);

Date currentTime = new Date();
dialog.setTitle("Showned at: "+currentTime.toLocaleString());

}

@Override
protected Dialog onCreateDialog(int id) {
return DialogFactory.getDialogById(id, this);
}

/**
* Вызывается по нажатию на кнопку,
* после чего в зависимости от нажатой
* кнопки покажет нужный диалог
*
* @param view
*/
public void onButtonClicked(View view) {
int buttonid = view.getId();
int dialogId = 0;

switch (buttonid) {
case R.id.alert_dlg:
dialogId = DialogFactory.DIALOG_ALERT;
break;
case R.id.input_dlg:
dialogId = DialogFactory.DIALOG_INPUT;
break;
case R.id.progress_dlg:
dialogId = DialogFactory.DIALOG_PROGRESS;
break;
}

showDialog(dialogId);

}
}

В методе onPrepareDialog() я сделал смену заголовка диалога, чтобы сделать более наглядным его вызов. Как видите, метод onCreateDialog() очень сократился за счет переноса всей логики в фабричный метод. Красиво. Мне нравится =)

Конечно картина не будет полной, если я не опишу Layout:


<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:gravity="center_vertical"
android:stretchColumns="0,1,2">

<TextView
android:textStyle="bold"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:gravity="center"
android:text="Нажми кнопку и получи диалог в подарок!"/>

<TableRow
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:padding="10dp">

<Button
android:id="@+id/alert_dlg"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Alert"
android:onClick="onButtonClicked"/>
<Button
android:id="@+id/progress_dlg"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Progress"
android:onClick="onButtonClicked"/>
<Button
android:id="@+id/input_dlg"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="Input"
android:onClick="onButtonClicked"/>


</TableRow>

</TableLayout>

Ну а «общая картина» будет такой:

Краткое заключение

Мы научились создавать 3 типа диалогов, передавать управление ими в Activity и тем самым сокращать себе работу.

Исходники: ConstructiveDialogI.

Буду рад отзывам и комментариям!

Happy Coding! =)

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *