4.1: Using Keyboards, Input Controls, Alerts, and Pickers

Contents:

You can customize input methods to make entering data easier for users.

In this practical, you'll learn to:

  • Use different on-screen keyboards and controls for user input.
  • Show an alert message that users can interact with.
  • Provide interface elements for selecting a time and date.
  • Use images as buttons to launch an activity.
  • Add radio buttons for the user to select one item from a set of items.

What you should already KNOW

For this practical you should be able to:

  • Create an Android Studio project from a template and generating the main layout.
  • Run apps on the emulator or a connected device.
  • Make a copy of an app project, and renaming the app.
  • Create and editing UI elements using the Layout Editor and XML code.
  • Access UI elements from your code using findViewById().
  • Convert the text in a view to a string using getText().toString().
  • Handle a button click.
  • Display a toast message.
  • Start an activity with another app using an implicit intent.
  • Use an adapter to connect your data to a view, such as the RecyclerView in a previous lesson.

What you will LEARN

In this practical, you will learn to:

  • Change the input methods to enable spelling suggestions, auto-capitalization, and password obfuscation.
  • Change the generic on-screen keyboard to a phone keypad or other specialized keyboards.
  • Add a spinner input control to show a dropdown menu with values, from which the user can select one.
  • Add an alert with OK and Cancel for a user decision.
  • Use date and time pickers and recording the selections.
  • Use images as buttons to launch an activity.
  • Add radio buttons for the user to select one item from a set of items.

What you will DO

  • Create new Android Studio projects to show keyboards, a spinner, an alert, and time and date pickers.
  • Provide spelling suggestions when a user enters text, and automatically capitalize new sentences, by experimenting with the input method.
  • Experiment with the input type attribute to change the on-screen keyboard to a special keyboard for entering email addresses, and then to a numeric keypad to force numeric entry.
  • Add a spinner input control for the phone number field for selecting one value from a set of values.
  • Create a new project with an alert dialog to notify the user to make a decision, such as OK or Cancel.
  • Add the date picker and time picker to the new project, and use listeners to record the user's selection.
  • Create a new project to use images as buttons.
  • Create a second activity and add radio buttons for selecting an option.
  • Set onClick handlers for the images used as buttons to launch a second activity.

App overview

In this practical, you'll create and build a new app called Keyboard Samples for experimenting with the android:inputType attribute for the EditText UI element. You will change the keyboard so that it suggests spelling corrections and capitalizes each new sentence, as shown on the left side of the figure below. To keep the app simple, you'll display the entered text in a toast message, shown on the right side of the figure below. Sentence Capitalization (left) and Toast Message (right)

You will also change the keyboard to one that offers the "@" symbol in a prominent location for entering email addresses, and to a phone keypad for entering phone numbers, as shown on the left side and in the center of the figure below. As a challenge, you will implement a listener for the action key in the keyboard in order to send an implicit intent to another app to dial the phone number.

You will then copy the app to create Phone Number Spinner that offers a spinner input control for selecting the label (Home, Work, Other, Custom) for the phone number, as shown on the right side of the figure below. Email Keyboard (left) – Phone Keyboard (center) – Spinner (right)

The figure above shows the following:

  1. The email keyboard with the "@" symbol in an easy-to-find location
  2. The phone keypad
  3. The spinner

You'll also create Alert Sample to experiment with an alert dialog, shown on the left side of the figure below, and Date Time Pickers to experiment with a date picker and a time picker, shown in the center and on the right side of the figure below, and use the time and date selections in your app. Alert Dialog (left) – Date Picker (center) – and Time Picker (right)

The last tasks involve creating an app from the Basic Activity template that lets a user tap image buttons to launch an activity, as shown on the left side of the figure below, and choose a single delivery option from radio-button choices for a food order, as shown on the right side of the figure below. Images as Buttons (left) and Radio Buttons (right)

Task 1. Experiment with text entry keyboard attributes

Touching an EditText editable text field places the cursor in the text field and automatically displays the on-screen keyboard. You will change attributes of the text entry field so that the keyboard suggests spelling corrections while you type, and automatically starts each new sentence with capital letters. For example:

  • android:inputType="textCapSentences": Sets the keyboard to capital letters at the beginning of sentences.
  • android:inputType="textAutoCorrect": Sets the keyboard to show automatic spelling corrections as you enter characters.
  • android:inputType="textMultiLine": Enables the Return key on the keyboard to end lines and create new blank lines without closing the keyboard.
  • android:inputType="textPassword": Sets the characters the user enters into dots to conceal the entered password.

1.1 Create the main layout and the showText method

You will add a Button, and change the TextView element to an EditText element so that the user can enter text. The app's layout will look like the following figure. Keyboard Samples App

  1. Create a new project called Keyboard Samples, and choose the Empty Activity template.
  2. Open the activity_main.xml layout file. In the Layout Editor, click the Text tab at the bottom of the screen and change the root view group to RelativeLayout, as you've done in previous exercises.
  3. Add a Button above the existing TextView element with the following attributes:
    Button attribute New value
    android:id "@+id/button_main"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:layout_alignParentBottom "true"
    android:layout_alignParentRight "true"
    android:onClick "showText"
    android:text "Show"
  4. Extract the string resource for the android:text attribute value to create and entry for it in strings.xml: Place the cursor on "Show", press Alt-Enter (Option-Enter on the Mac), and select Extract string resources. Then change the Resource name for the string value to show.

    You extract string resources because it makes the app project more flexible for changing strings. The string resource assignments are stored in the strings.xml file (under app > res > values). You can edit this file to change the string assignments so that the app can be localized with a different language. For example, the "Show" value for the resource named show could be changed to "Montrer" for the French version of the app.

  5. Change the existing TextView element as follows:
    1. Delete the android:text attribute that specified "Hello World!".
    2. If the TextView element includes any layout-constraint attributes, remove them.
    3. Change the TextView tag to an EditText tag, and make sure the ending tag is />.
    4. Add or change the following attributes:
      EditText attribute TextView old value EditText new value
      android:id "@+id/editText_main"
      android:layout_width "wrap_content" "match_parent"
      android:layout_height "wrap_content" "wrap_content"
      android:layout_alignParentBottom "true"
      android:layout_toLeftOf "@id/button_main"
      android:hint "Enter a message"
      You learned about the android:layout_toLeftOf and android:layout_alignParentBottom attributes in a previous lesson. These layout-related attributes work with the RelativeLayout view group to position child views relative to each other or to the parent. The android:hint attribute sets the text to appear in the field that provides a hint for the user to provide input, such as "Enter a message"
  6. Extract the string resource for the android:hint attribute value "Enter a message" to the resource name enter. Depending on your version of Android Studio, your activity_main.xml layout file will look something like the following:

    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.example.android.keyboardsamples.MainActivity">
    
        <Button
            android:id="@+id/button_main"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:onClick="showText"
            android:text="@string/show" />
    
        <EditText
            android:id="@+id/editText_main"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_toLeftOf="@id/button_main"
            android:hint="@string/enter" />
    
    </RelativeLayout>
    
  7. Open MainActivity.java and enter the following showText method, which retrieves the information entered into the EditText element and shows it in a toast message:
    public void showText(View view) {
       EditText editText = (EditText) findViewById(R.id.editText_main);
       if (editText != null) {
          String showString = editText.getText().toString();
          Toast.makeText(this, showString, Toast.LENGTH_SHORT).show();
       }
    }
    
  8. Open strings.xml (in app > res > values), and edit the app_name value to "Keyboard Samples" (be sure to include a space between "Keyboard" and "Samples").
  9. Run the app and examine how the keyboard works.

Tapping the Show button shows the toast message of the text entry.

To close the on-screen keyboard, tap the down-pointing arrow in the bottom row of icons.

In the standard keyboard layout, a checkmark icon in a green circle, shown below, appears in the lower right corner of the keypad. This is known as the Return (or Enter) key, and it is used to enter a new line: The Return (Enter) Key on the Keyboard

With the default attributes for the EditText element, tapping the Return key adds another line of text. In the next section, you will change the keyboard so that it capitalizes sentences as you type. As a result of setting the android:inputType attribute, the default attribute for the Return key changes to shift focus away from the EditText element and close the keyboard.

1.2 Set the keyboard to capitalize sentences

  1. Add the android:inputType attribute to the EditText element using the textCapSentences value to set the keyboard to capital letters at the beginning of a sentence, so that users can automatically start a sentence with a capital letter:
    android:inputType="textCapSentences"
    
  2. Run your app.

Capital letters will now appear on the keyboard at the beginning of sentences. When you tap the Return key on the keyboard, the keyboard closes and your text entry is finished. You can still tap the text entry field to add more text or edit the text. Tap Show to show the text in a toast message.

For details about the android:inputType attribute, see Specifying the Input Method Type.

1.3 Set the keyboard to hide a password when entering it

  1. Change the EditText element to use the textPassword value for the android:inputType attribute.
    android:inputType="textPassword"
    
  2. Change the android:hint to "Enter your password".
  3. Run the app.

The characters the user enters turn into dots to conceal the entered password. For help, see Text Fields.

Solution code:

Android Project: KeyboardSamples

Task 2. Change the keyboard type

Every text field expects a certain type of text input, such as an email address, phone number, password, or just plain text. It's important to specify the input type for each text field in your app so that the system displays the appropriate soft input method, such as:

  • The standard on-screen keyboard for plain text
  • The keyboard for an email address which includes the "@" symbol in a prominent location
  • The phone keypad for a phone number

2.1 Use an email keyboard

Modify the main activity's EditText element to show an email keyboard rather than a standard keyboard:

  1. In the EditText element in the activity_main.xml layout file, change the android:inputType attribute to the following:
    android:inputType="textEmailAddress"
    
  2. Change the android:hint attribute to "Enter an email address".
  3. Extract the string resource for the android:hint value to enter_email.
  4. Run the app. Tapping the field brings up the on-screen email keyboard with the "@" symbol located next to the space key.

2.2 Use a phone keypad

Modify the main activity's EditText element to show a phone keypad rather than a standard keyboard:

  1. In the EditText element in the activity_main.xml layout file, change the android:inputType attribute to the following:
    android:inputType="phone"
    
  2. Change the android:hint attribute to "Enter a phone number".
  3. Extract the string resource for the android:hint value to enter_phone.
  4. Run the app.

Tapping the field now brings up the on-screen phone keypad in place of the standard keyboard.

Note: When running the app on the emulator, the field will still accept text rather than numbers if you type on the computer's keyboard. However, when run on the device, the field only accepts the numbers of the keypad.

Task 3. Add a spinner input control for selecting a phone label

Input controls are the interactive components in your app's user interface. Android provides a wide variety of controls you can use in your UI, such as buttons, seek bars, checkboxes, zoom buttons, toggle buttons, spinners, and many more. (For more information about input controls, see Input Controls.)

A spinner provides a quick way to select one value from a set. Touching the spinner displays a drop-down list with all available values, from which the user can select one. If you are providing only two or three choices, you might want to use radio buttons for the choices if you have room in your layout for them; however, with more than three choices, a spinner works very well, scrolls as needed to display items, and takes up little room in your layout.

For more information about spinners, see Spinners.

To provide a way to select a label for a phone number (such as Home, Work, Mobile, and Other), you can add a spinner to the layout to appear right next to the phone number field.

3.1 Copy the KeyboardSamples project and modify the layout

Use the following figure as as a guide for the main activity's layout: Layout for Phone Number Spinner

In the above figure:

  1. The first LinearLayout with an EditText view, a spinner icon, and the Show button.
  2. The second LinearLayout with two TextViews.

Follow these steps:

  1. Copy the KeyboardSamples project folder, rename it to PhoneNumberSpinner, and refactor it to populate the new name throughout the app project. (See the Appendix for instructions on copying a project.)
  2. After refactoring, change the <string name="app_name"> value in the strings.xml file (within app > res > values) to Phone Number Spinner (with spaces) as the app's name.
  3. Open the activity_main.xml layout file.
  4. Enclose the existing EditText and Button elements from the previous lesson within a LinearLayout with a horizontal orientation, placing the EditText element above the Button:

    <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_marginTop="@dimen/activity_vertical_margin"
       android:orientation="horizontal">
    
       <EditText
          …
       <Button
          …
    </LinearLayout>
    
  5. Make the following changes to the EditText and Button elements:
    1. Remove the following attributes from the EditText element:
      • android:layout_toLeftOf
      • android:layout_alignParentBottom
    2. Remove the following attributes from the Button element:
      • android:layout_alignParentRight
      • android:layout_alignParentBottom
    3. Change three other attributes of the EditText element as follows:
      EditText attribute Value
      android:layout_width "wrap_content"
      android:inputType "phone"
      android:hint "Enter phone number"
  6. Add a Spinner element between the EditText element and the Button element:

    <Spinner
       android:id="@+id/label_spinner"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content">
    </Spinner>
    

    The Spinner element provides the drop-down list. In the next task you will add code that will fill the spinner list with values. The layout code for the EditText, Spinner, and Button elements within the LinearLayout should now look like this:

    <LinearLayout
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_marginTop="@dimen/activity_vertical_margin"
       android:orientation="horizontal">
    
       <EditText
          android:id="@+id/editText_main"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:inputType="phone"
          android:hint="Enter phone number" />
    
       <Spinner
          android:id="@+id/label_spinner"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content">
       </Spinner>
    
       <Button
          android:id="@+id/button_main"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:onClick="showText"
          android:text="Show" />
    
    </LinearLayout>
    
  7. Add another LinearLayout below the LinearLayout you just created, with a horizontal orientation to enclose two TextView elements side-by-side — a text description, and a text field to show the phone number and the phone label — and align the LinearLayout to the parent's bottom (refer to the figure above):

    <LinearLayout
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       android:layout_alignParentBottom="true">
    
       <TextView
          …
       <TextView
          …
    </LinearLayout>
    
  8. Add the following TextView elements within the LinearLayout:
    TextView attribute Value
    android:id "@+id/title_phonelabel"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:text "Phone Number: "
    TextView attribute Value
    android:id "@+id/text_phonelabel"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:text "Nothing entered."
  9. Check your layout by clicking the Preview tab on the right side of the layout window. Preview of the Layout You should now have a screen (refer to the figure above) with the phone entry field at the top on the left, a skeletal spinner next to the field, and the Show button on the right. At the bottom should appear the text "Phone Number:" followed by "Nothing entered."
  10. Extract your strings into string resources: Place the cursor on the hard-coded string, press Alt-Enter (Option-Enter on the Mac), and select Extract string resources. Then edit the Resource name for the string value. Extract as follows:
    Element String String resource
    EditText "Enter phone number" "@string/hint_phonenumber"
    Button "Show" "@string/show_button"
    TextView "Phone Number: " "@string/phonenumber_label"
    TextView "Nothing entered." "@string/nothing_entered"

3.2 Add code to activate the spinner and its listener

The choices for this phone label spinner are well-defined static strings ("Home", "Work", etc), so you can use a text array defined in strings.xml to hold the values for it.

To activate the spinner and its listener, implement the AdapterView.OnItemSelectedListener interface, which requires also adding the onItemSelected() and onNothingSelected() callback methods.

  1. Open strings.xml to define the selectable values (Home, Work, Mobile, and Other) for the spinner as the string array labels_array:
    <string-array name="labels_array">
            <item>Home</item>
            <item>Work</item>
            <item>Mobile</item>
            <item>Other</item>
    </string-array>
    
  2. To define the selection callback for the spinner, change your MainActivity class to implement the AdapterView.OnItemSelectedListener interface as shown:
    public class MainActivity extends AppCompatActivity implements
                AdapterView.OnItemSelectedListener {
    
    As you type AdapterView. in the above statement, Android Studio automatically imports the AdapterView widget. The reason why you need the AdapterView is because you need an adapter—specifically an ArrayAdapter—to assign the array to the spinner. An adapter connects your data—in this case, the array of spinner items—to the spinner view. You will learn more about this pattern of using an adapter to connect data in another lesson. This line should appear in your block of import statements:
    import android.widget.AdapterView;
    
    After typing OnItemSelectedListener in the above statement, wait a few seconds for a red light bulb to appear in the left margin.
  3. Click the bulb and choose Implement methods. The onItemSelected() and onNothingSelected() methods, which are required for OnItemSelectedListener, should already be highlighted, and the "Insert @Override" option should be checked. Click OK.

    This step automatically adds empty onItemSelected() and onNothingSelected() callback methods to the bottom of the MainActivity class. Both methods use the parameter AdapterView<?>. The <?> is a Java type wildcard, enabling the method to be flexible enough to accept any type of AdapterView as an argument.

  4. Instantiate a spinner object in the onCreate() method using the Spinner element in the layout (label_spinner), and set its listener (spinner.setOnItemSelectedListener) in the onCreate() method. Add the code to the onCreate() method:
    @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...
       // Create the spinner.
       Spinner spinner = (Spinner) findViewById(R.id.label_spinner);
       if (spinner != null) {
                spinner.setOnItemSelectedListener(this);
       }
       ...
    
  5. Continuing to edit the onCreate() method, add a statement that creates the ArrayAdapter with the string array (labels_array) using the Android-supplied simple spinner layout for each item (layout.simple_spinner_item):
    ...
    // Create ArrayAdapter using the string array and default spinner layout.
    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
                R.array.labels_array, android.R.layout.simple_spinner_item);
    ...
    
    The simple_spinner_item layout used in this step, and the simple_spinner_dropdown_item layout used in the next step, are the default pre-defined layouts provided by Android in the R.layout class. You should use these layouts unless you want to define your own layouts for the items in the spinner and the spinner's appearance.
  6. Specify the layout for the spinner's choices to be simple_spinner_dropdown_item, and then apply the adapter to the spinner:
       ...
       // Specify the layout to use when the list of choices appears.
       adapter.setDropDownViewResource
                           (android.R.layout.simple_spinner_dropdown_item);
       // Apply the adapter to the spinner.
       if (spinner != null) {
                spinner.setAdapter(adapter);
       }
       ...
    

3.3 Add code to respond to the user's selections

When the user selects an item in the spinner, the Spinner object receives an on-item-selected event. To handle this event, you already implemented the AdapterView.OnItemSelectedListener interface in the previous step, adding empty onItemSelected() and onNothingSelected() callback methods.

In this step you will first declare mSpinnerLabel as the string to hold the selected item. You will then fill in the code for the onItemSelected() method to retrieve the selected item in the spinner, using getItemAtPosition(), and assign the item to mSpinnerLabel:

  1. Declare the mSpinnerLabel string at the beginning of the MainActivity class definition:
    public class MainActivity extends AppCompatActivity implements
                   AdapterView.OnItemSelectedListener {
     private String mSpinnerLabel = "";
     ...
    }
    
  2. Add code to the empty onItemSelected() callback method, as shown below, to retrieve the user's selected item using getItemAtPosition, and assign it to mSpinnerLabel. You can also add a call to the showText() method you already added to the previous version of the app:

    public void onItemSelected(AdapterView<?> adapterView, View view, int
                   i, long l) {
       mSpinnerLabel = adapterView.getItemAtPosition(i).toString();
       showText(view);
    }
    

    Tip: By adding the showText() method to the above onItemSelected() method, you have enabled the spinner selection listener to display the spinner choice along with the phone number, so that you no longer need the Show button that called the showText() method.

  3. Add code to the empty onNothingSelected() callback method, as shown below, to display a logcat message if nothing is selected:

    public void onNothingSelected(AdapterView<?> adapterView) {
        Log.d(TAG, "onNothingSelected: ");
    }
    

    The TAG in the above statement is in red because it hasn't been defined.

  4. Extract the string resource for "onNothingSelected: " to nothing_selected.

  5. Click TAG, click the red light bulb, and choose Create constant field 'TAG' from the pop-up menu. Android Studio adds the following under the MainActivity class declaration:

private static final String TAG = ;
  1. Add MainActivity.class.getSimpleName() to use the simple name of the class for TAG:
private static final String TAG = MainActivity.class.getSimpleName();
  1. Change the String showString statement in the showText method to show both the entered string and the selected spinner item (mSpinnerLabel):
String showString = (editText.getText().toString() + " - " + mSpinnerLabel);
  1. Run the app.

The spinner appears next to the phone entry field and shows the first choice (Home). Tapping the spinner reveals all the choices, as shown on the left side of the figure below. After entering a phone number and choosing a spinner item, a message appears at the bottom of the screen with the phone number and the selected spinner item, as shown on the right side of the figure below. (You can also tap the Show button to show both the phone number and the spinner item, but since this is redundant, you can now remove the Show button.)

Choosing from a Spinner (left) - and Showing the Choice (right)

Solution code:

Android Studio project: PhoneNumberSpinner

Task 4. Use a dialog for an alert requiring a decision

You can provide a dialog for an alert to require users to make a decision. A dialog is a window that appears on top of the display or fills the display, interrupting the flow of activity.

For example, an alert dialog might require the user to click Continue after reading it, or give the user a choice to agree with an action by clicking a positive button (such as OK or Accept), or to disagree by clicking a negative button (such as Cancel). In Android, you use the AlertDialog subclass of the Dialog class to show a standard dialog for an alert.

Tip: Use dialogs sparingly as they interrupt the user's work flow. Read the Dialogs design guide for best design practices, and Dialogs in the Android developer documentation for code examples.

In this practical, you will use a button to trigger a standard alert dialog. In a real world app, you might trigger an alert dialog based on some condition, or based on the user tapping something.

Android Studio project: AlertSample

4.1 Create a new project with a layout to show an alert dialog

In this exercise, you'll build an alert with OK and Cancel buttons, which will be triggered by the user clicking a button.

  1. Create a new project called Alert Sample based on the Empty Activity template.
  2. Open the activity_main.xml layout file. In the Layout Editor, click the Text tab at the bottom of the screen and change the root view group to RelativeLayout, as you've done in previous exercises.
  3. If the TextView element includes any layout-constraint attributes, remove them.
  4. Make the following changes to the TextView:
    TextView attribute Value
    android:id "@+id/top_message"
    android:text "Tap to test the alert:"
  5. Extract the android:text string above into the resource tap_test to make it easier to translate.
  6. Add a Button with the following attributes:
    Button attribute Value
    android:id "@+button1"
    android:layout_width wrap_content
    android:layout_height wrap_content
    android:layout_below "@id/top_message"
    android:layout_marginTop "36dp"
    android:text "Alert"
    android:onClick "onClickShowAlert"
  7. Extract the android:text string above into the resource alert_button to make it easier to translate.
  8. Extract the dimension value for android:layout_marginTop the same way: Place the cursor on "36dp", press Alt-Enter (Option-Enter on the Mac), and select Extract dimension resource. Then edit the Resource name for the value to button_top_margin.

The dimension resource assignments are stored in the dimens.xml file (under app > res > values > dimens). You can edit this file to change the assignments so that the app can be changed for different display sizes.

4.2 Add an alert dialog to the main activity

The builder design pattern makes it easy to create an object from a class that has a lot of required and optional attributes and would therefore require a lot of parameters to build. Without this pattern, you would have to create constructors for combinations of required and optional attributes; with this pattern, the code is easier to read and maintain. For more information about the builder design pattern, see Builder pattern.

The builder class is usually a static member class of the class it builds. You use AlertDialog.Builder to build a standard alert dialog, using setTitle to set its title, setMessage to set its message, and setPositiveButton and setNegativeButton to set its buttons.

To make the alert, you need to make an object of AlertDialog.Builder. You will add the onClickShowAlert() method, which makes this object as its first order of business.

Note: To keep this example simple to understand, the alert dialog is created in the onClickShowAlert() method. This occurs only if the onClickShowAlert() method is called, which is what happens when the user clicks the button. This means the app builds a new dialog only when the button is clicked, which is useful if the dialog is seen only rarely (when the user takes a certain path through the app). However, if the dialog appears often, you may want to build the dialog once in the onCreate() method, and then invoke the dialog in the onClickShowAlert() method.
  1. Add the onClickShowAlert() method to MainActivity.java as follows:

    public void onClickShowAlert(View view) {
      AlertDialog.Builder myAlertBuilder = new
                    AlertDialog.Builder(MainActivity.this);
    
    Note: If AlertDialog.Builder is not recognized as you enter it, click the red light bulb icon, and choose the support library version (android.support.v7.app.AlertDialog) for importing into your activity.
  2. Set the title and the message for the alert dialog inside onClickShowAlert() after the code in the previous step:

    ...
    // Set the dialog title.
    myAlertBuilder.setTitle("Alert");
    // Set the dialog message.
    myAlertBuilder.setMessage("Click OK to continue, or Cancel to stop:");
    ...
    
  3. Extract the title and message into string resources. The previous lines of code should now be:
    ...
    // Set the dialog title.
    myAlertBuilder.setTitle(R.string.alert_title);
    // Set the dialog message.
    myAlertBuilder.setMessage(R.string.alert_message);
    ...
    
  4. Add the OK button to the alert with setPositiveButton() and using onClickListener():

    ...
    // Add the buttons.
    myAlertBuilder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
              // User clicked OK button.
              Toast.makeText(getApplicationContext(), "Pressed OK",
                      Toast.LENGTH_SHORT).show();
         }
    });
    ...
    

    You set the positive (OK) and negative (Cancel) buttons using the setPositiveButton() and setNegativeButton() methods. After the user taps the OK button in the alert, you can grab the user's selection and use it in your code. In this example, you display a toast message if the OK button is clicked.

  5. Extract the string resource for "OK" and for "Pressed OK". The statement should now be:

    ...
    // Add the buttons.
    myAlertBuilder.setPositiveButton(R.string.ok, new
                          DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             // User clicked OK button.
             Toast.makeText(getApplicationContext(), R.string.pressed_ok,
                          Toast.LENGTH_SHORT).show();
         }
    });
    ...
    
  6. Add the Cancel button to the alert with setNegativeButton() and onClickListener(), display a toast message if the button is clicked, and then cancel the dialog:
    ...
    myAlertBuilder.setNegativeButton("Cancel", new      
                           DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
              // User cancelled the dialog.
              Toast.makeText(getApplicationContext(), "Pressed Cancel",
                           Toast.LENGTH_SHORT).show();
         }
    });
    ...
    
  7. Extract the string resource for "Cancel" and "Pressed Cancel". The statement should now be:
    ...
    myAlertBuilder.setNegativeButton(R.string.cancel, new
                           DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int which) {
             // User cancelled the dialog.
             Toast.makeText(getApplicationContext(), R.string.pressed_cancel,
                           Toast.LENGTH_SHORT).show();
         }
    });
    ...
    
  8. Add show(), which creates and then displays the alert dialog, to the end of onClickShowAlert():

      ...
      // Create and show the AlertDialog.
      myAlertBuilder.show();
    }
    

    Tip: To learn more about onClickListener and other listeners, see User Interface: Input Events.

  9. Run the app.

You should be able to tap the Alert button, shown on the left side of the figure below, to see the alert dialog, shown in the center of the figure below. The dialog shows OK and Cancel buttons, and a toast message appears showing which one you pressed, as shown on the right side of the figure below. Main Activity Button (left) - Alert Dialog (center) - and Toast Showing the Pressed Button (right)

Solution code:

Android Studio project: AlertSample

Task 5. Use a picker for user input

Android provides ready-to-use dialogs, called pickers, for picking a time or a date. You can use them to ensure that your users pick a valid time or date that is formatted correctly and adjusted to the user's local time and date. Each picker provides controls for selecting each part of the time (hour, minute, AM/PM) or date (month, day, year). You can read all about setting up pickers in Pickers.

In this task you'll create a new project, and add the date picker and time picker. You will also learn how to use fragments. A fragment is a behavior or a portion of user interface within an activity. It's like a mini-activity within the main activity, with its own own lifecycle, and it's used for building a picker. All the work is done for you. To learn about fragments, see Fragments in the API Guide.

One benefit of using fragments for the pickers is that you can isolate the code sections for managing the date and the time for various locales that display date and time in different ways. The best practice to show a picker is to use an instance of DialogFragment, which is a subclass of Fragment. A DialogFragment displays a dialog window floating on top of its activity's window. In this exercise, you'll add a fragment for each picker dialog and use DialogFragment to manage the dialog lifecycle.

Tip: Another benefit of using fragments for the pickers is that you can implement different layout configurations, such as a basic dialog on handset-sized displays or an embedded part of a layout on large displays.

5.1 Create the main activity layout

To start this task, create the main activity layout to provide buttons to access the time and date pickers. Refer to the XML layout code below:

  1. Start a new project called Date Time Pickers using the Empty Activity template.
  2. Open activity_main.xml to edit the layout code.
  3. Change the layout to LinearLayout and add android:orientation="vertical" to orient the layout vertically. Don't worry about the appearance of the layout yet. The goal is to use a layout that embeds a RelativeLayout within the LinearLayout: Diagram of the New Main Layout
  4. Change the first TextView element's text to "Choose the date and time: " and extract the text to the string resource choose_datetime.
    TextView attribute Old value New value
    android:text "Hello World" "@string/choose_datetime"
  5. Add the android:textSize attribute and enter a text size of 20sp. Extract the android:textSize dimension to text_size.
    TextView attribute Old value New value
    android:textSize "@dimen/text_size"
  6. Add a RelativeLayout child inside the LinearLayout to contain the Button elements, and accept the "match parent" default width and height.
  7. Add the first Button element within the RelativeLayout with the following attributes:
    First Button attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:id "@+id/button_date"
    android:layout_marginTop "12dp"
    android:text "Date"
    android:onClick "showDatePickerDialog"
    Don't worry that the showDatePickerDialog reference is in red. The method hasn't been defined yet—you define it later.
  8. Extract the string "Date" into the string resource date_button.
  9. Extract the dimension "12dp" for android:layout_marginTop to button_top_margin.
  10. Add the second Button element inside the RelativeLayout child with the following attributes:
    Second Button attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:id "@+id/button_time"
    android:layout_marginTop "@dimen/button_top_margin"
    android:layout_alignBottom "@id/button_date"
    android:layout_toRightOf "@id/button_date"
    android:text "Time"
    android:onClick "showTimePickerDialog"
    The showTimePickerDialog reference is in red. The method hasn't been defined yet — you define it later.
  11. Extract the string "Time" into the string resource time_button.
  12. If you haven't already done so, click the Preview tab to show a preview of the layout. It should look like the code and figure below.

Solution code for the main layout:

Depending on your version of Android Studio, your code will look something like the following.

<?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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.android.DateTimePickers.MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/text_size"
        android:text="@string/choose_datetime"/>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button_date"
            android:layout_marginTop="@dimen/button_top_margin"
            android:text="@string/date_button"
            android:onClick="showDatePickerDialog"/>

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/button_time"
            android:layout_marginTop="@dimen/button_top_margin"
            android:layout_alignBottom="@id/button_date"
            android:layout_toRightOf="@id/button_date"
            android:text="@string/time_button"
            android:onClick="showTimePickerDialog"/>

    </RelativeLayout>
</LinearLayout>

Main Activity Layout

5.2 Create a new fragment for the date picker

In this exercise, you'll add a fragment for the date picker. A fragment is like a mini-activity within the main activity, with its own own lifecycle.

  1. Expand app > java > com.example.android.DateTimePickers and select MainActivity.
  2. Choose File > New > Fragment > Fragment (Blank), and name the fragment DatePickerFragment. Uncheck all three checkbox options so that you do not create a layout XML, do not include fragment factory methods, and do not include interface callbacks. You don't need to create a layout for a standard picker. Click Finish to create the fragment.
  3. Open DatePickerFragment and edit the DatePickerFragment class definition to extend DialogFragment and implement DatePickerDialog.OnDateSetListener to create a standard date picker with a listener. See Picker for more information about extending DialogFragment for a date picker:
     public class DatePickerFragment extends DialogFragment
                     implements DatePickerDialog.OnDateSetListener {
    
    As you type DialogFragment and DatePickerDialog.OnDateSetListener, Android Studio automatically adds the following in the import block at the top:
    import android.app.DatePickerDialog;
    import android.support.v4.app.DialogFragment;
    
    In addition, a red bulb icon appears in the left margin after a few seconds.
  4. Click the red bulb icon and choose Implement methods from the pop-up menu. A dialog appears with onDateSet() already selected and the "Insert @Override" option checked. Click OK to create the empty onDateSet() method. This method will be called when the user sets the date. After adding the empty onDateSet() method, Android Studio automatically adds the following in the import block at the top:
    import android.widget.DatePicker;
    
    The onDateSet() method's parameters should be int year, int month, and int dayOfMonth. Change the dayOfMonth parameter to day for brevity:
    public void onDateSet(DatePicker view, int year, int month, int day)
    
  5. Remove the empty public constructor for DatePickerFragment.
  6. Replace onCreateView() with onCreateDialog() that returns Dialog, and annotate onCreateDialog() with @NonNull to indicate that the return value Dialog can't be null—any attempt to refer to the return value Dialog must be null-checked.
      @NonNull
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
          ...
      }
    
  7. Add the following code to onCreateDialog() to initialize the year, month, and day from Calendar, and return the dialog and these values to the main activity. As you enter Calendar, specify the import to be java.util.Calendar.

      @NonNull
      @Override
      public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Use the current date as the default date in the picker.
         final Calendar c = Calendar.getInstance();
         int year = c.get(Calendar.YEAR);
         int month = c.get(Calendar.MONTH);
         int day = c.get(Calendar.DAY_OF_MONTH);
    
         // Create a new instance of DatePickerDialog and return it.
         return new DatePickerDialog(getActivity(), this, year, month, day);
      }
    

Solution code for DatePickerFragment:

public class DatePickerFragment extends DialogFragment
        implements DatePickerDialog.OnDateSetListener {

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current date as the default date in the picker.
        final Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);

        // Create a new instance of DatePickerDialog and return it.
        return new DatePickerDialog(getActivity(), this, year, month, day);
    }

    public void onDateSet(DatePicker view, int year, int month, int day) {
        // Do something with the date chosen by the user.
    }

}

5.3 Create a new fragment for the time picker

Add a fragment to the DateTimePickers project for the time picker:

  1. Select MainActivity again.
  2. Choose File > New > Fragment > Fragment (Blank), and name the fragment TimePickerFragment. Uncheck all three options so you do not create a layout XML, do not include fragment factory methods, and do not include interface callbacks. Click Finish to create the fragment.
  3. Open TimePickerFragment and follow the same procedures as with DatePickerFragment, implementing the onTimeSet() blank method, replacing onCreateView() with onCreateDialog(), and removing the empty public constructor for TimePickerFragment. TimePickerFragment performs the same tasks as the DatePickerFragment, but with time values:
    • It extends DialogFragment and implements TimePickerDialog.OnTimeSetListener to create a standard time picker with a listener. See Picker for more information about extending DialogFragment for a time picker.
    • It uses the onCreateDialog() method to initialize the hour and minute from Calendar, and returns the dialog and these values to the main activity using the 24-hour date format. As you enter Calendar, specify the import to be java.util.Calendar.
    • It also defines the empty onTimeSet() method for you to add code to use the hourOfDay and minute the user selects. This method will be called when the user sets the time:
      public void onTimeSet(TimePicker view,
                             int hourOfDay, int minute) {
      // Do something with the time chosen by the user.
      }
      
Note: As you make the changes, Android Studio automatically adds the following in the import block at the top:
import android.support.v4.app.DialogFragment;
import android.app.TimePickerDialog;
import android.widget.TimePicker;
import java.util.Calendar;

Solution code for TimePickerFragment:

public class TimePickerFragment extends DialogFragment
        implements TimePickerDialog.OnTimeSetListener {

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker.
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);

        // Create a new instance of TimePickerDialog and return it.
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user.
    }
}

5.4 Modify the main activity

While much of the code in the main activity stays the same, you need to add methods that create instances of FragmentManager to manage each fragment and show each picker.

  1. Create string resources in strings.xml:
    <string name="date_picker">datePicker</string>
    <string name="time_picker">timePicker</string>
    
  2. Open MainActivity.
  3. Add the showDatePickerDialog() and showTimePickerDialog() methods, referring to the code below. It creates an instance of FragmentManager to manage the fragment automatically, and to show the picker. For more information about fragments, see Fragments.

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void showDatePickerDialog(View v) {
            DialogFragment newFragment = new DatePickerFragment();
            newFragment.show(getSupportFragmentManager(),
                        getString(R.string.date_picker));
        }
    
        public void showTimePickerDialog(View view) {
            DialogFragment newFragment = new TimePickerFragment();
            newFragment.show(getSupportFragmentManager(),
                        getString(R.string.time_picker));
        }
    }
    
  4. Run the app. You should see the date and time pickers after tapping the buttons. Date Picker (left) and Time Picker (right)

5.5 Use the chosen date and time

In this exercise you'll pass the date and time back to MainActivity, and convert the date and time to strings that you can show in a toast message.

  1. Open MainActivity and add the processDatePickerResult() method signature that takes the year, month, and day as arguments:
    public void processDatePickerResult(int year, int month, int day) {
    }
    
  2. Add the following code to the processDatePickerResult() method to convert the month, day, and year to separate strings:
    String month_string = Integer.toString(month+1);
    String day_string = Integer.toString(day);
    String year_string = Integer.toString(year);
    
    Tip: The month integer returned by the date picker starts counting at 0 for January, so you need to add 1 to it to start show months starting at 1.
  3. Add the following after the above code to concatenate the three strings and include slash marks for the U.S. date format:
    String dateMessage = (month_string + "/" +
                                day_string + "/" + year_string);
    
  4. Add the following after the above statement to display a toast message:
    Toast.makeText(this, "Date: " + dateMessage,
                                Toast.LENGTH_SHORT).show();
    
  5. Extract the hard-coded string "Date: " into a string resource named date. This automatically replaces the hard-coded string with getString(R.string.date). The code for the processDatePickerResult() method should now look like this:
    public void processDatePickerResult(int year, int month, int day) {
       String month_string = Integer.toString(month + 1);
       String day_string = Integer.toString(day);
       String year_string = Integer.toString(year);
       // Assign the concatenated strings to dateMessage.
       String dateMessage = (month_string + "/" +
                                     day_string + "/" + year_string);
       Toast.makeText(this, getString(R.string.date) + dateMessage,
                                     Toast.LENGTH_SHORT).show();
    }
    
  6. Open DatePickerFragment, and add the following to the onDateSet() method to invoke the processDatePickerResult() method in MainActivity and pass it the year, month, and day:
    public void onDateSet(DatePicker view, int year, int month, int day) {
       // Set the activity to the Main Activity.
       MainActivity activity = (MainActivity) getActivity();
       // Invoke Main Activity's processDatePickerResult() method.
       activity.processDatePickerResult(year, month, day);
    }
    
    You use getActivity() which, when used in a fragment, returns the activity the fragment is currently associated with. You need this because you can't call a method in MainActivity without the context of MainActivity (you would have to use an intent instead, as you learned in a previous lesson). The activity inherits the context, so you can use it as the context for calling the method (as in activity.processDatePickerResult).
  7. The TimePickerFragment uses the same logic. Open MainActivity and add the processTimePickerResult() method signature that takes the hourOfDay and minute as arguments:
    public void processTimePickerResult(int hourOfDay, int minute) {
    }
    
  8. Add the following code to the processTimePickerResult() method to convert the hourOfDay and minute to separate strings:
    String hour_string = Integer.toString(hourOfDay);
    String minute_string = Integer.toString(minute);
    
  9. Add the following after the above code to concatenate the strings and include a colon for the time format:
    String timeMessage = (hour_string + ":" + minute_string);
    
  10. Add the following after the above statement to display a toast message:
    Toast.makeText(this, "Time: " + timeMessage,
                                Toast.LENGTH_SHORT).show();
    
  11. Extract the hard-coded string "Time: " into a string resource named time. This automatically replaces the hard-coded string with getString(R.string.time). The code for the processDatePickerResult() method should now look like this:
    public void processTimePickerResult(int hourOfDay, int minute) {
       // Convert time elements into strings.
       String hour_string = Integer.toString(hourOfDay);
       String minute_string = Integer.toString(minute);
       // Assign the concatenated strings to timeMessage.
       String timeMessage = (hour_string + ":" + minute_string);
       Toast.makeText(this, getString(R.string.time) + timeMessage,
                                      Toast.LENGTH_SHORT).show();
    }
    
  12. Open TimePickerFragment and add the following to the onTimeSet() method to invoke the processTimePickerResult() method in MainActivity and pass it the hourOfDay and minute:
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
       // Set the activity to the Main Activity.
       MainActivity activity = (MainActivity) getActivity();
       // Invoke Main Activity's processTimePickerResult() method.
       activity.processTimePickerResult(hourOfDay, minute);
    }
    
  13. You can now run the app. After selecting the date or time, the date or time appears in a toast message at the bottom, as shown in the figure below. Date Selection Appears in Toast at Bottom

Solution code:

Android Studio project: DateTimePickers

Task 6: Use image views as buttons

You can make a view clickable, as a button, by adding the android:onClick attribute in the XML layout. For example, you can make an image act like a button by adding android:onClick to the ImageView.

Tip: If you are using multiple images as clickable images, arrange them in a viewgroup so that they are grouped together.

In this task you'll create a prototype of an app for ordering desserts from a café. After starting a new project based on the Basic Activity template, you'll modify the "Hello World" TextView with appropriate text, and add images to use for the "Add to order" buttons.

6.1 Start the new project

  1. Start a new Android Studio project with the app name Droid Cafe. Choose the Basic Activity template, accept the default settings, and click Finish. The project opens with two layouts in the res > layout folder: activity_main.xml, and content_main.xml.
  2. Open the content_main.xml layout file. In the Layout Editor, click the Text tab at the bottom of the screen and change the root view group to RelativeLayout, as you've done in previous exercises.
  3. Open content_main.xml. If the TextView element includes any layout-constraint attributes, remove them. Extract the "Hello World" string in the TextView to use the intro_text resource name.
  4. Open strings.xml and redefine the intro_text resource to use more descriptive text, such as "Droid Desserts":
    <string id="intro_text">Droid Desserts</string>
    
  5. Change the TextView in the layout to use a larger text size of 24sp and padding of 10dp, and add the android:id attribute with the id set to textintro.
  6. Extract the dimension resource for the android:padding attribute to the resource name padding_regular, and the android:textSize attribute to the resource name text_heading. You will use these resource names in subsequent steps.
  7. Add another TextView under the textintro TextView with the following attributes:
    TextView attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_regular"
    android:id "@+id/choose_dessert"
    android:layout_below "@id/textintro"
    android:text "Choose a dessert."
  8. Extract the string resource for the android:text attribute to the resource name choose_a_dessert.

6.2 Add the images

  1. The images named donut_circle.jpg, froyo_circle.jpg, and icecream_circle.jpg are provided with the starter apps in the 4_1_P_starter_images.zip file, which you can unzip on your computer. To copy the images to your project, follow these steps:
    1. Close your project.
    2. Copy the image files into your project's drawable folder. Find the drawable folder in a project by using this path: project_name > app > src > main > res > drawable
    3. Reopen your project.
  2. Open content_main.xml file again and add an ImageView for the donut image to the layout under the choose_dessert view, using the following attributes:
    ImageView attribute for donut Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_regular"
    android:id "@+id/donut"
    android:layout_below "@id/choose_dessert"
    android:contentDescription "Donuts are glazed and sprinkled with candy."
    android:src "@drawable/donut_circle"
  3. Extract the android:contentDescription attribute value to the string resource donuts. You will use this string resource in the next step.
  4. Add a TextView that will appear next to the donut image as a description, with the following attributes:
    TextView attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "35dp"
    android:layout_below "@+id/choose_dessert"
    android:layout_toRightOf "@id/donut"
    android:text "@string/donuts"
  5. Extract the dimension resource for the android:padding attribute to the resource name padding_wide. You will use this resource name in subsequent steps.
  6. Add a second ImageView to the layout for the ice cream sandwich, using the following attributes:
    ImageView attribute for ice_cream Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_regular"
    android:id "@+id/ice_cream"
    android:layout_below "@id/donut"
    android:contentDescription "Ice cream sandwiches have chocolate wafers and vanilla filling."
    android:src "@drawable/icecream_circle"
  7. Extract the android:contentDescription attribute value to the string resource ice_cream_sandwiches.
  8. Add a TextView that will appear next to the ice cream sandwich as a description, with the following attributes:
    TextView attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_wide"
    android:layout_below "@+id/donut"
    android:layout_toRightOf "@id/ice_cream"
    android:text "@string/ice_cream_sandwiches"
  9. Add a third ImageView to the layout for the froyo, using the following attributes:
    ImageView attribute for ice_cream Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_regular"
    android:id "@+id/froyo"
    android:layout_below "@id/ice_cream"
    android:contentDescription "FroYo is premium self-serve frozen yogurt."
    android:src "@drawable/froyo_circle"
  10. Extract the android:contentDescription attribute value to the string resource froyo.
  11. Add a TextView that will appear next to the froyo as a description, with the following attributes:
    TextView attribute Value
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:padding "@dimen/padding_wide"
    android:layout_below "@+id/ice_cream"
    android:layout_toRightOf "@id/froyo"
    android:text "@string/froyo"

6.3 Add onClick methods for the image views

You can add the android:onClick attribute to any View to make it clickable as a button. In this step you will add android:onClick to the images in the content_main.xml layout. You need to also add a method for the android:onClick attribute to call. The method, for this task, displays a toast message showing which image was tapped. (In a later task, you will modify the method to launch another activity called OrderActivity.)

  1. Add the following string resources to the strings.xml file for the strings to be shown in the toast message:
    <string name="donut_order_message">You ordered a donut.</string>
    <string name="ice_cream_order_message">You ordered an ice cream sandwich.</string>
    <string name="froyo_order_message">You ordered a FroYo.</string>
    
  2. Add the following displayToast() method for displaying a toast message:
    public void displayToast(String message) {
       Toast.makeText(getApplicationContext(), message,
                              Toast.LENGTH_SHORT).show();
    }
    
  3. Add the following showFoodOrder() method to the end of MainActivity (before the closing bracket). For this task, use the displayToast() method to display a toast message:
    /**
    * Displays a toast message for the food order
    * and starts the OrderActivity activity.
    * @param message   Message to display.
    */
    public void showFoodOrder(String message) {
       displayToast(message);
    }
    
    Tip: The first four lines are a comment in the Javadoc format, which makes the code easier to understand and also helps generate documentation for your code if you use Javadoc. It is a best practice to add such a comment to every new method you create. For more information about how to write comments, see How to Write Doc Comments for the Javadoc Tool.

Although you could have added this method in any position within MainActivity, it is best practice to put your own methods below the methods already provided in MainActivity by the template.

  1. Add the following methods to the end of MainActivity (you can add them before showFoodOrder()):

    /**
    * Shows a message that the donut image was clicked.
    */
    public void showDonutOrder(View view) {
       showFoodOrder(getString(R.string.donut_order_message));
    }
    
    /**
    * Shows a message that the ice cream sandwich image was clicked.
    */
    public void showIceCreamOrder(View view) {
       showFoodOrder(getString(R.string.ice_cream_order_message));
    }
    
    /**
    * Shows a message that the froyo image was clicked.
    */
    public void showFroyoOrder(View view) {
       showFoodOrder(getString(R.string.froyo_order_message));
    }
    
  2. Add the android:onClick attribute to the three ImageViews in content_main.xml:
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:id="@+id/donut"
            android:layout_below="@id/choose_dessert"
            android:contentDescription="@string/donut"
            android:src="@drawable/donut_circle"
            android:onClick="showDonutOrder"/>
    . . .
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:id="@+id/ice_cream"
            android:layout_below="@id/donut"
            android:contentDescription="@string/ice_cream_sandwich"
            android:src="@drawable/icecream_circle"
            android:onClick="showIceCreamOrder"/>
    . . .
    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:id="@+id/froyo"
            android:layout_below="@id/ice_cream"
            android:contentDescription="@string/froyo"
            android:src="@drawable/froyo_circle"
            android:onClick="showFroyoOrder"/>
    
  3. Run the app.

    Clicking the donut, ice cream sandwich, or froyo image displays a toast message about the order, as shown in the figure below. Clicking an Image Displays a Toast

Task 7: Use radio buttons

Radio buttons are input controls that are useful for selecting only one option from a set of options. You should use radio buttons if you want the user to see all available options side-by-side. If it's not necessary to show all options side-by-side, you may want to use a spinner instead.

Later in this practical you will add another activity and screen layout for setting the delivery options for a food order, and use radio buttons for the delivery choices.

For an overview and more sample code for radio buttons, see Radio Buttons.

7.1 Add another activity

As you learned in a previous lesson, an activity represents a single screen in your app in which your user can perform a single, focused task. You already have one activity, MainActivity.java. You will now add another activity for setting the delivery options for an order, and use an explicit intent to launch the second activity.

  1. Right-click the com.example.android.droidcafe folder in the left column and choose New > Activity > Empty Activity. Edit the Activity Name to be OrderActivity, and the Layout Name to be activity_order. Leave the other options alone, and click Finish.

    The OrderActivity class should now be listed under MainActivity in the java folder, and activity_order.xml should now be listed in the layout folder. The Empty Activity template added these files.

  2. Open the activity_order.xml layout file. In the Layout Editor, click the Text tab at the bottom of the screen and change the root view group to RelativeLayout, as you've done in previous exercises.
  3. Open MainActivity. Change the showFoodOrder() method to make an explicit intent to start OrderActivity:
    public void showFoodOrder(String message) {
            displayToast(message);
            Intent intent = new Intent(this, OrderActivity.class);
            startActivity(intent);
    }
    
  4. Run the app. Clicking an image button now launches the second activity, which is a blank screen. (The toast message appears briefly on the blank screen.)

7.2 Add the layout for radio buttons

To create each radio button option, you will create RadioButton elements in the activity_order.xml layout file, which is linked to OrderActivity. After editing the layout file, the layout for the radio buttons in OrderActivity will look something like the figure below, depending on your version of Android Studio. Layout for Radio Buttons

Since radio button selections are mutually exclusive, you will group them together inside a RadioGroup. By grouping them together, the Android system ensures that only one radio button can be selected at a time.

Note: The order in which you list the RadioButton elements determines the order that they appear on the screen.
  1. Open activity_order.xml_ac and add a TextView element with the id set to order_intro_text:
    TextView attribute Value
    android:id "@+id/order_intro_text"
    android:layout_width "match_parent"
    android:layout_height "wrap_content"
    android:layout_marginTop "24dp"
    android:layout_marginBottom "6dp"
    android:textSize "18sp"
    android:text "Choose a delivery method:"
  2. Extract the string resource for "Choose a delivery method:" to be choose_delivery_method.
  3. Extract the dimension resources for the margin values:
  4. "24dp" to text_margin_top
  5. "6dp" to text_margin_bottom
  6. "18sp" to intro_text_size
  7. Add a RadioGroup to the layout underneath the TextView you just added:
    <RadioGroup
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical"
       android:layout_below="@id/order_intro_text">
    </RadioGroup>
    
  8. Add the following three RadioButton elements within the RadioGroup, using the following attributes. The "onRadioButtonClicked" entry for the onClick attribute will be highlighted until you add that method in the next task.
    RadioButton #1 attribute Value
    android:id "@+id/sameday"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:text "Same day messenger service"
    android:onClick "onRadioButtonClicked"
    RadioButton #2 attribute Value
    android:id "@+id/nextday"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:text "Next day ground delivery"
    android:onClick "onRadioButtonClicked"
    RadioButton #3 attribute Value
    android:id "@+id/pickup"
    android:layout_width "wrap_content"
    android:layout_height "wrap_content"
    android:text "Pick up"
    android:onClick "onRadioButtonClicked"
  9. Extract the three string resources for the android:text attributes to the following names, so that the strings can be easily translated:
    • same_day_messenger_service
    • next_day_ground_delivery
    • pick_up

7.3 Add the radio button click handler

The android:onClick attribute for each radio button element specifies the onRadioButtonClicked() method to handle the click event. Therefore, you need to add a new onRadioButtonClicked() method in the OrderActivity class.

Ordinarily your app would display some message regarding which type of delivery was chosen. You will accomplish this with a toast message by creating a method called displayToast() in OrderActivity.

In the onRadioButtonClicked() method you will use a switch case block to check if a radio button has been clicked. At the end of the switch case block, you will add a default statement that displays a log message if none of the radio buttons were checked.

  1. Open strings.xml and create the following string resources:
    1. A resource named chosen for the string "Chosen: " (include the space after the colon and the quotation marks).
    2. A resource named nothing_clicked for the string "onRadioButtonClicked: Nothing clicked."
  2. Open OrderActivity and add the following statement to define TAG_ACTIVITY for the log message:
    private static final String TAG_ACTIVITY =
                                       OrderActivity.class.getSimpleName();
    
  3. Add the following displayToast method to OrderActivity:
    public void displayToast(String message) {
       Toast.makeText(getApplicationContext(), message,
                             Toast.LENGTH_SHORT).show();
    }
    
  4. Add the following onRadioButtonClicked() method, which checks to see if a radio button has been checked, and uses a switch case block to determine which radio button item was selected, in order to set the appropriate message for that item to use with displayToast():
    public void onRadioButtonClicked(View view) {
       // Is the button now checked?
       boolean checked = ((RadioButton) view).isChecked();
       // Check which radio button was clicked
       switch(view.getId()) {
          case R.id.sameday:
             if (checked)
                // Same day service
                displayToast(getString(R.string.chosen) +
                                getString(R.string.same_day_messenger_service));
             break;
          case R.id.nextday:
             if (checked)
                // Next day delivery
                displayToast(getString(R.string.chosen) +
                                getString(R.string.next_day_ground_delivery));
             break;
          case R.id.pickup:
             if (checked)
                // Pick up
                   displayToast(getString(R.string.chosen) +
                                getString(R.string.pick_up));
             break;
          default:
             Log.d(TAG_ACTIVITY, getString(R.string.nothing_clicked));
             break;
       }
    }
    
  5. Run the app. Tap an image to see the OrderActivity activity, which shows the delivery choices. Tap a delivery choice, and you will see a toast message at the bottom of the screen with the choice, as shown in the figure below. The OrderActivity with Delivery Choices

Solution code

Android Studio project: DroidCafe Part 1

Coding challenge

Note: All coding challenges are optional and are not prerequisites for later lessons.

Challenge: You can also perform an action directly from the keyboard and replace the Return (Enter) key with a "send" key, such as for dialing a phone number: Send Key on Keyboard

For this challenge, use the android:imeOptions attribute for the EditText component with the actionSend value:

android:imeOptions="actionSend"

In the onCreate() method for this main activity, you can use setOnEditorActionListener() to set the listener for the EditText view to detect if the key is pressed:

EditText editText = (EditText) findViewById(R.id.editText_main);
if (editText != null)
   editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
      ...
   });

For help setting the listener, see "Specifying the Input Action" in Handling Keyboard Input and "Specifying Keyboard Actions" in Text Fields.

The next step is to override onEditorAction() and use the IME_ACTION_SEND constant in the EditorInfo class to respond to the pressed key. In the example below, the key is used to call the dialNumber() method to dial the phone number:

@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
   boolean mHandled = false;
   if (actionId == EditorInfo.IME_ACTION_SEND) {
      dialNumber();
      mHandled = true;
   }
   return mHandled;
}

To finish the challenge, create the dialNumber() method, which uses an implicit intent with ACTION_DIAL to pass the phone number to another app that can dial the number. It should look like this:

private void dialNumber() {
   EditText editText = (EditText) findViewById(R.id.editText_main);
   String mPhoneNum = null;
   if (editText != null) mPhoneNum = "tel:" + editText.getText().toString();
   Log.d(TAG, "dialNumber: " + mPhoneNum);
   Intent intent = new Intent(Intent.ACTION_DIAL);
   intent.setData(Uri.parse(mPhoneNum));
   if (intent.resolveActivity(getPackageManager()) != null) {
      startActivity(intent);
   } else {
       Log.d("ImplicitIntents", "Can't handle this!");
   }
}

Summary

In this practical, you learned how to:

  • Set up XML layout attributes to control the keyboard for an EditText element:
    • Use the textAutoCorrect value for the android:inputType attribute to change the keyboard so that it suggests spelling corrections.
    • Use the textCapSentences value for the android:inputType attribute to start each new sentence with a capital letter.
    • Use the textPassword value for the android:inputType attribute to hide a password when entering it.
    • Use the textEmailAddress value for the android:inputType attribute to show an email keyboard rather than a standard keyboard.
    • Use the phone value for the android:inputType attribute to show a phone keypad rather than a standard keyboard.
    • Challenge: Use the android:imeOptions attribute with the actionSend value to perform an action directly from the keyboard and replace the Return key with an action key, such as an implicit intent to another app to dial a phone number.
  • Use a Spinner input control to provide a drop-down menu, and write code to control it:
    • Use an ArrayAdapter to assign an array of text values as the spinner menu items.
    • Implement the AdapterView.OnItemSelectedListener interface, which requires also adding the onItemSelected() and onNothingSelected() callback methods to activate the spinner and its listener.
    • Use the onItemSelected() callback method to retrieve the selected item in the spinner menu using getItemAtPosition.
  • Use AlertDialog.Builder, a subclass of AlertDialog, to build a standard alert dialog, using setTitle to set its title, setMessage to set its message, and setPositiveButton and setNegativeButton to set its buttons.
  • Use the standard date and time pickers:
    • Add a fragment for a date picker, and extend the DialogFragment class to implement DatePickerDialog.OnDateSetListener for a standard date picker with a listener.
    • Add a fragment for a time picker, and extend the DialogFragment class to implement TimePickerDialog.OnTimeSetListener for a standard time picker with a listener.
    • Implement the onDateSet(), onTimeSet(), and onCreateDialog() methods.
    • Use the onFinishDateDialog() and onFinishTimeDialog() methods to retrieve the selected date and time.
  • Use images in a project:
    • Copy an image into the project, and define an ImageView element to use it.
    • Add the android:onClick attribute to make the ImageView elements clickable like buttons. You can make any View clickable with the android:onClick attribute.
  • Use radio buttons:
    • Create a second activity.
    • Add RadioButton elements within a RadioGroup in the second activity.
    • Create radio button handlers.
    • Launch the second activity from an image click.

The related concept documentation is in Android Developer Fundamentals: Concepts.

Learn more

results matching ""

    No results matching ""