Урок 110. Android 3. Fragments. DialogFragment - диалог


В этом уроке:

- работаем с DialogFragment

Продолжаем рассматривать наследников Fragment. DialogFragment – отличается от обычного фрагмента тем, что отображается как диалог и имеет соответствующие методы.

Построить диалог можно двумя способами: используя свой layout-файл и через AlertDialog.Builder. Нарисуем приложение, которое будет вызывать два диалога, построенных разными способами.


Создадим проект:

Project name: P1101_DialogFragment
Build Target: Android 4.1
Application name: DialogFragment
Package name: ru.startandroid.develop.p1101dialogfragment
Create Activity: MainActivity


Добавим строки в strings.xml:

<string name="dialog_1">Dialog 1</string>
<string name="dialog_2">Dialog 2</string>
<string name="message_text">Text of your message</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="maybe">Maybe</string>

Мы будем создавать два диалога, соответственно нам понадобятся два фрагмента.

Создадим layout-файл для первого фрагмента.

dialog1.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="20dp"
android:text="@string/message_text"
android:textAppearance="?android:attr/textAppearanceLarge">
</TextView>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/btnYes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/yes">
</Button>
<Button
android:id="@+id/btnNo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/no">
</Button>
<Button
android:id="@+id/btnMaybe"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="@string/maybe">
</Button>
</LinearLayout>
</LinearLayout>

Так будет выглядеть наш диалог – текст сообщения и три кнопки.


Создаем класс Dialog1.java:

package ru.startandroid.develop.p1101dialogfragment;

import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;

public class Dialog1 extends DialogFragment implements OnClickListener {

 
final String LOG_TAG = "myLogs";

 
public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState
) {
   
getDialog().setTitle("Title!");
    View v = inflater.inflate
(R.layout.dialog1, null);
    v.findViewById
(R.id.btnYes).setOnClickListener(this);
    v.findViewById
(R.id.btnNo).setOnClickListener(this);
    v.findViewById
(R.id.btnMaybe).setOnClickListener(this);
   
return v;
 
}

 
public void onClick(View v) {
   
Log.d(LOG_TAG, "Dialog 1: " + ((Button) v).getText());
    dismiss
();
 
}

 
public void onDismiss(DialogInterface dialog) {
   
super.onDismiss(dialog);
    Log.d
(LOG_TAG, "Dialog 1: onDismiss");
 
}

 
public void onCancel(DialogInterface dialog) {
   
super.onCancel(dialog);
    Log.d
(LOG_TAG, "Dialog 1: onCancel");
 
}
}

В onCreateView мы получаем объект Dialog с помощью метода getDialog и устанавливаем заголовок диалога. Далее мы создаем view из layout, находим в нем кнопки и ставим им текущий фрагмент в качестве обработчика.

В onClick выводим в лог текст нажатой кнопки и сами явно закрываем диалог методом dismiss.

Метод onDismiss срабатывает, когда диалог закрывается. Пишем об этом в лог.

Метод onCancel срабатывает, когда диалог отменяют кнопкой Назад. Пишем об этом в лог.


Создаем второй фрагмент. Здесь мы будем строить диалог с помощью билдера, поэтому layout-файл не понадобится. Создаем только класс Dialog2.java:

package ru.startandroid.develop.p1101dialogfragment;

import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.os.Bundle;
import android.util.Log;

public class Dialog2 extends DialogFragment implements OnClickListener {

 
final String LOG_TAG = "myLogs";

 
public Dialog onCreateDialog(Bundle savedInstanceState) {
   
AlertDialog.Builder adb = new AlertDialog.Builder(getActivity())
       
.setTitle("Title!").setPositiveButton(R.string.yes, this)
       
.setNegativeButton(R.string.no, this)
       
.setNeutralButton(R.string.maybe, this)
       
.setMessage(R.string.message_text);
   
return adb.create();
 
}

 
public void onClick(DialogInterface dialog, int which) {
   
int i = 0;
   
switch (which) {
   
case Dialog.BUTTON_POSITIVE:
      i = R.string.yes;
     
break;
   
case Dialog.BUTTON_NEGATIVE:
      i = R.string.no;
     
break;
   
case Dialog.BUTTON_NEUTRAL:
      i = R.string.maybe;
     
break;
   
}
   
if (i > 0)
     
Log.d(LOG_TAG, "Dialog 2: " + getResources().getString(i));
 
}

 
public void onDismiss(DialogInterface dialog) {
   
super.onDismiss(dialog);
    Log.d
(LOG_TAG, "Dialog 2: onDismiss");
 
}

 
public void onCancel(DialogInterface dialog) {
   
super.onCancel(dialog);
    Log.d
(LOG_TAG, "Dialog 2: onCancel");
 
}
}

Обычно для заполнения фрагмента содержимым мы использовали метод onCreateView. Для создания диалога с помощью билдера используется onCreateDialog. Создаем диалог с заголовком, сообщением и тремя кнопками. Обработчиком для кнопок назначаем текущий фрагмент.

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

Методы onDismiss и onCancel – это закрытие и отмена диалога, аналогично первому фрагменту.


Меняем layout-файл для MainActivity - main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/btnDlg1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/dialog_1">
</Button>
<Button
android:id="@+id/btnDlg2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="@string/dialog_2">
</Button>
</LinearLayout>

Здесь только две кнопки.


Кодим MainActivity.java:

package ru.startandroid.develop.p1101dialogfragment;

import android.app.Activity;
import android.app.DialogFragment;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

 
DialogFragment dlg1;
  DialogFragment dlg2;

 
@Override
 
public void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);
    setContentView
(R.layout.main);
    dlg1 =
new Dialog1();
    dlg2 =
new Dialog2();
 
}

 
public void onClick(View v) {
   
switch (v.getId()) {
   
case R.id.btnDlg1:
      dlg1.show
(getFragmentManager(), "dlg1");
     
break;
   
case R.id.btnDlg2:
      dlg2.show
(getFragmentManager(), "dlg2");
     
break;
   
default:
     
break;
   
}

  }
}

Создаем диалоги и запускаем их методом show, который на вход требует FragmentManager и строку-тэг. Транзакция и коммит происходят внутри этого метода, нам об этом думать не надо.

Все сохраняем и запускаем приложение.


Жмем Dialog1

Отобразился наш простенький диалог.

Жмем какую-нибудь кнопку, например, Yes - диалог закрылся. Смотрим логи:

Dialog 1: Yes
Dialog 1: onDismiss

Все верно.


Снова запустим первый диалог и нажмем клавишу Назад (Back). Смотрим лог:

Dialog 1: onCancel
Dialog 1: onDismiss

Сработал onCancel – диалог был отменен, и onDismiss – диалог закрылся.

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


Запустим второй диалог – нажмем кнопку Dialog 2.

Отобразился стандартный сконструированный нами диалог. Жмем, например, No – диалог закрылся. В логах:

Dialog 2: No
Dialog 2: onDismiss


Снова запустим второй диалог и нажмем Назад. В логах:

Dialog 2: onCancel
Dialog 2: onDismiss

Все так же, как и в первом случае.


Еще несколько слов по теме.

Если вы не хотите, чтобы ваш диалог можно было закрыть кнопкой, используйте для вашего диалог-фрагмента метод setCancelable с параметром false.

Есть еще один вариант вызова диалога. Это метод show, но на вход он уже принимает не FragmentManager, а FragmentTransaction. В этом случае система также сама вызовет commit внутри show, но мы можем предварительно поместить в созданную нами транзакцию какие-либо еще операции или отправить ее в BackStack.

Вы можете использовать диалог-фрагменты, как обычные фрагменты и отображать их на Activity, а не в виде диалога. Но при этом будьте аккуратнее с использованием getDialog. Я так понял, что он возвращает null в этом случае.

Если AlertDialog.Builder вам незнаком, то посмотрите Урок 60 и несколько следующих за ним. Там достаточно подробно описано, как создавать различные диалоги.


На следующем уроке:

- работаем с PreferenceFragment
- используем Headers