The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.


Dialogs and DialogFragments

Generally speaking, modal dialogs are considered to offer poor UX, particularly on mobile devices. You want to give the user more choices, not fewer, and so locking them into “deal with this dialog right now, or else” is not especially friendly. That being said, from time to time, there will be cases where that sort of modal interface is necessary, and to help with that, Android does have a dialog framework that you can use.

Prerequisites

Understanding this chapter requires that you have read the core chapters of this book.

DatePickerDialog and TimePickerDialog

Android has a pair of built-in dialogs that handle the common operations of allowing the user to select a date (DatePickerDialog) or a time (TimePickerDialog). These are simply dialog wrappers around the DatePicker and TimePicker widgets, as are described in this book’s Widget Catalog.

The DatePickerDialog allows you to set the starting date for the selection, in the form of a year, month, and day of month value. Note that the month runs from 0 for January through 11 for December. Most importantly, both let you provide a callback object (OnDateChangedListener or OnDateSetListener) where you are informed of a new date selected by the user. It is up to you to store that date someplace, particularly if you are using the dialog, since there is no other way for you to get at the chosen date later on.

Similarly, TimePickerDialog lets you:

For example, from the Dialogs/Chrono sample project, here’s a trivial layout containing a label and two buttons — the buttons will pop up the dialog flavors of the date and time pickers:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  >
  <TextView android:id="@+id/dateAndTime" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content"
    />
  <Button android:id="@+id/dateBtn" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:text="Set the Date"
    android:onClick="chooseDate"
    />
  <Button android:id="@+id/timeBtn" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:text="Set the Time"
    android:onClick="chooseTime"
    />
</LinearLayout>

The more interesting stuff comes in the Java source:

package com.commonsware.android.chrono;

import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.DatePicker;
import android.widget.TextView;
import android.widget.TimePicker;
import java.util.Calendar;

public class ChronoDemo extends Activity {
  TextView dateAndTimeLabel;
  Calendar dateAndTime=Calendar.getInstance();
      
  @Override
  public void onCreate(Bundle state) {
    super.onCreate(state);
    setContentView(R.layout.main);
    
    dateAndTimeLabel=(TextView)findViewById(R.id.dateAndTime);
    
    updateLabel();
  }
  
  public void chooseDate(View v) {
    new DatePickerDialog(this, d,
                          dateAndTime.get(Calendar.YEAR),
                          dateAndTime.get(Calendar.MONTH),
                          dateAndTime.get(Calendar.DAY_OF_MONTH))
      .show();
  }
  
  public void chooseTime(View v) {
    new TimePickerDialog(this, t,
                          dateAndTime.get(Calendar.HOUR_OF_DAY),
                          dateAndTime.get(Calendar.MINUTE),
                          true)
      .show();
  }
  
  private void updateLabel() {
    dateAndTimeLabel
      .setText(DateUtils
                 .formatDateTime(this,
                                 dateAndTime.getTimeInMillis(),
                                 DateUtils.FORMAT_SHOW_DATE|DateUtils.FORMAT_SHOW_TIME));
  }
  
  DatePickerDialog.OnDateSetListener d=new DatePickerDialog.OnDateSetListener() {
    public void onDateSet(DatePicker view, int year, int monthOfYear,
                          int dayOfMonth) {
      dateAndTime.set(Calendar.YEAR, year);
      dateAndTime.set(Calendar.MONTH, monthOfYear);
      dateAndTime.set(Calendar.DAY_OF_MONTH, dayOfMonth);
      updateLabel();
    }
  };
  
  TimePickerDialog.OnTimeSetListener t=new TimePickerDialog.OnTimeSetListener() {
    public void onTimeSet(TimePicker view, int hourOfDay,
                          int minute) {
      dateAndTime.set(Calendar.HOUR_OF_DAY, hourOfDay);
      dateAndTime.set(Calendar.MINUTE, minute);
      updateLabel();
    }
  };  
}

The “model” for this activity is just a Calendar instance, initially set to be the current date and time. In the updateLabel() method, we take the current Calendar, format it using DateUtils and formatDateTime(), and put it in the TextView. The nice thing about using Android’s DateUtils class is that it will format dates and times using the user’s choice of date formatting, determined through the Settings application.

Each button has a corresponding method that will get control when the user clicks it (chooseDate() and chooseTime()). When the button is clicked, either a DatePickerDialog or a TimePickerDialog is shown. In the case of the DatePickerDialog, we give it an OnDateSetListener callback that updates the Calendar with the new date (year, month, day of month). We also give the dialog the last-selected date, getting the values out of the Calendar. In the case of the TimePickerDialog, it gets an OnTimeSetListener callback to update the time portion of the Calendar, the last-selected time, and a true indicating we want 24-hour mode on the time selector

With all this wired together, the resulting activity looks like this:

ChronoDemo, As Initially Launched, on Android 7.1
Figure 468: ChronoDemo, As Initially Launched, on Android 7.1

ChronoDemo, Showing DatePickerDialog
Figure 469: ChronoDemo, Showing DatePickerDialog

ChronoDemo, Showing TimePickerDialog
Figure 470: ChronoDemo, Showing TimePickerDialog

Changes and Bugs

Android 4.1 through 4.4 have some changes in behavior from what came before and what came after.

First, the “Cancel” button was removed, unless you specifically add a negative button listener to the underlying DatePicker or TimePicker widget:

ChronoDemo, Showing DatePickerDialog, on Android 4.1
Figure 471: ChronoDemo, Showing DatePickerDialog, on Android 4.1

The user can press BACK to exit the dialog, so all functionality is still there, but you may need to craft your documentation to accommodate this difference. And, on Android 5.0+, the Cancel button returned.

Second, your OnDateSetListener or OnTimeSetListener will be called an extra time. If the user presses BACK to leave the dialog, your onDateSet() or onTimeSet() will be called. If the user clicks the positive button of the dialog, you are called twice. There is a workaround documented on Stack Overflow. This too was repaired in Android 5.0.

AlertDialog

The preview of this section is en route to Mars.

DialogFragments

The preview of this section will not appear here for a while, due to a time machine mishap.

DialogFragment: The Other Flavor

The preview of this section is in an invisible, microscopic font.

Dialogs: Modal, Not Blocking

The preview of this section was last seen in the Bermuda Triangle.