This practical codelab is part of Unit 4: User experience in Mobile Development You will get the most value out of this course if you work through the codelabs in sequence:
For details about the course, including links to all the concept chapters, apps, codelabs and slides, see Blackboard.
Introduction
To enable the user to enter text or numbers, you use an EditText element. Some input controls are EditText attributes that define the type of keyboard that appears, to make entering data easier for users. For example, you might choose phone for the android:inputType attribute to show a numeric keypad instead of an alphanumeric keyboard.
Other input controls make it easy for users to make choices. For example, RadioButton elements enable a user to select one (and only one) item from a set of items.
In this practical, you use attributes to control the on-screen keyboard appearance, and to set the type of data entry for an EditText. You also add radio buttons to the DroidCafe app so the user can select one item from a set of items.
You should be able to:
findViewById().View to a string using getText().Button click.Toast message.onClick handler for the radio buttons.In this practical, you add more features to the DroidCafe app from the lesson on using clickable images.
In the app's OrderFragment you experiment with the android:inputType attribute for EditText elements. You add EditText elements for a person's name and address, and use attributes to define single-line and multiple-line elements that make suggestions as you enter text. You also add an EditText that shows a numeric keypad for entering a phone number.
Other types of input controls include interactive elements that provide user choices. You add radio buttons to DroidCafe for choosing only one delivery option from several options. You also offer a spinner input control for selecting the label (Home, Work, Other, Custom) for the phone number.

Touching an EditText editable text field places the cursor in the text field and automatically displays the on-screen keyboard so that the user can enter text.
An editable text field expects a certain type of text input, such as plain text, an email address, a phone number, or a password. 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 an on-screen keyboard for plain text, or a numeric keypad for entering a phone number.
In this step you add a TextView and an EditText to the OrderFragment layout in the DroidCafe app so that the user can enter a person's name.
FrameLayout. Replace the FrameLayout element with a Constraintlayout. The code should look like this:<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".OrderFragment">
<TextView
android:id="@+id/order_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="This is the Order Fragment" />
</androidx.constraintlayout.widget.ConstraintLayout>
layout_width and the layout_height of the existing TextView called order_text to set their values to wrap_content.TextView to the top of the layout and the left side to the left of the layout and set the margins on the top and left to 24.
TextView to the ConstraintLayout in fragment_order.xml under the order_textview element already in the layout. Use the following attributes for the new TextView:Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
android:text attribute value to create and entry for it called name_label_text in strings.xml.EditText element. To use the visual layout editor, drag a Plain Text element from the Palette pane to a position next to the name_labelTextView. Then enter name_text for the ID field, and constrain the left side and baseline of the element to the name_label element right side and baseline as shown in the figure below (you may need to right click the EditText and select Show Baseline before you can align the baselines):

EditText.android:hint attribute value to enter_name_hint. The following attributes should be set for the new EditText (add the layout_marginLeft attribute for compatibility with older versions of Android):Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
android:inputType attribute is set to textPersonName.
, which appears in the lower right corner of the keyboard. This is known as the Done key.In this step you add another EditText to the OrderFragment layout in the DroidCafe app so that the user can enter an address using multiple lines.
TextView under the name_label element already in the layout. Use the following attributes for the new TextView:Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
android:text attribute value to create and entry for it called address_label_text in strings.xml.EditText element. To use the visual layout editor, drag a Multiline Text element from the Palette pane to a position next to the address_label TextView. Then enter address_text for the ID field, and constrain the left side and baseline of the element to the address_label element right side and baseline as shown in the figure below:
EditText.android:hint attribute value to enter_address_hint. The following attributes should be set for the new EditText (add the layout_marginLeft attribute for compatibility with older versions of Android):<EditText
android:id="@+id/address_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:ems="10"
android:gravity="start|top"
android:hint="@string/enter_address_hint"
android:inputType="textMultiLine"
app:layout_constraintBaseline_toBaselineOf="@+id/address_label"
app:layout_constraintStart_toEndOf="@+id/address_label" />
Fragment.
in the lower right corner of the keyboard (also known as the Enter or New Line key) to start a new line of text. The Return key appears if you set the textMultiLine value for the android:inputType attribute.
In this step you add another EditText to the OrderFragment layout in the DroidCafe app so that the user can enter a phone number on a numeric keypad.
TextView under the address_label element already in the layout. Use the following attributes for the new TextView:Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TextView is constrained to the bottom of the multiple-line EditText (address_text). This is because address_text can grow to multiple lines, and this TextView should appear beneath it.android:text attribute value to create and entry for it called phone_label_text in strings.xml.EditText element. To use the visual layout editor, drag a Phone element from the Palette pane to a position next to the phone_labelTextView. Then enter phone_text for the ID field, and constrain the left side and baseline of the element to the phone_label element right side and baseline as shown in the figure below:
EditText.android:hint attribute value to enter_phone_hint. The following attributes should be set for the new EditText (add the layout_marginLeft attribute for compatibility with older versions of Android):<EditText
android:id="@+id/phone_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:ems="10"
android:hint="@string/enter_phone_hint"
android:inputType="phone"
app:layout_constraintBaseline_toBaselineOf="@+id/phone_label"
app:layout_constraintStart_toEndOf="@+id/phone_label" />
Fragment.
.To experiment with android:inputType attribute values, change an EditText element's android:inputType values to the following to see the result:
You can combine inputType attribute values that don't conflict with each other. For example, you can combine the textMultiLine and textCapSentences attribute values for multiple lines of text in which each sentence starts with a capital letter.
TextView under the phone_label element already in the layout. Use the following attributes for the new TextView:Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
android:text attribute value to create and entry for it called note_label_text in strings.xml.EditText element. To use the visual layout editor, drag a Multiline Text element from the Palette pane to a position next to the note_labelTextView. Then enter note_text for the ID field, and constrain the left side and baseline of the element to the note_label element right side and baseline as you did previously with the other EditText elements.inputType field in the Attributes pane. The textMultiLine value is already selected. In addition, select textCapSentences to combine these attributes.android:hint attribute value to enter_note_hint. The following attributes should be set for the new EditText (add the layout_marginLeft attribute for compatibility with older versions of Android):<EditText
android:id="@+id/note_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:ems="10"
android:hint="@string/enter_note_hint"
android:inputType="textMultiLine|textCapSentences"
app:layout_constraintBaseline_toBaselineOf="@+id/note_label"
app:layout_constraintStart_toEndOf="@+id/note_label" />
To combine values for the android:inputType attribute, concatenate them using the pipe (|) character.Fragment.
Input controls are the interactive elements in your app's UI that accept data input. Radio buttons are input controls that are useful for selecting only one option from a set of options.
In this task you add a group of radio buttons to the DroidCafeInput app for setting the delivery options for the dessert order. For an overview and more sample code for radio buttons, see Radio Buttons.
To add radio buttons to OrderFragment in the DroidCafeInput app, you create RadioButton elements in the fragement_order.xml layout file. After editing the layout file, the layout for the radio buttons in OrderFragment will look something like the figure below.
Because radio button selections are mutually exclusive, you 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.
fragment_order.xml and add a TextView element constrained to the bottom of the note_text element already in the layout, and to the left margin, as shown in the figure below.
Attribute | Value |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"Choose a delivery method:" to be choose_delivery_method.RadioGroup. Add the RadioGroup to the layout underneath the TextView you just added, enclosing three RadioButton elements as shown in the XML code below: <RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/delivery_label">
<RadioButton
android:id="@+id/sameday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Same day express delivery"
tools:layout_editor_absoluteX="30dp"
tools:layout_editor_absoluteY="370dp" />
<RadioButton
android:id="@+id/nextday"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Next day delivery"
tools:layout_editor_absoluteX="24dp"
tools:layout_editor_absoluteY="351dp" />
<RadioButton
android:id="@+id/pickup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Pick up"
tools:layout_editor_absoluteX="28dp"
tools:layout_editor_absoluteY="380dp" />
</RadioGroup>
android:text attributes to the following names so that the strings can be translated easily: same_day_express_delivery, next_day_delivery, and pick_up.OrderFragment. Add the following code to your onCreateView method in OrderFragment:RadioButton sameDay = v.findViewById(R.id.sameday);
sameDay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (checked) {
displayToast(getString(R.string.same_day_express_delivery));
}
}
});
RadioButton nextDay = v.findViewById(R.id.nextday);
nextDay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (checked) {
displayToast(getString(R.string.next_day_delivery));
}
}
});
RadioButton pickUp = v.findViewById(R.id.pickup);
pickUp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
boolean checked = ((RadioButton) view).isChecked();
if (checked) {
displayToast(getString(R.string.pick_up));
}
}
});
public void displayToast(String message) {
Toast.makeText(getContext(), message,
Toast.LENGTH_SHORT).show();
}
OrderFragment screen, which shows the delivery choices. Tap a delivery choice, and you see a Toast message at the bottom of the screen with the choice, as shown in the figure below.
Challenge: The radio buttons for delivery choices in the DroidCafeInput app first appear unselected, which implies that there is no default delivery choice. Change the radio buttons so that one of them (such as nextday) is selected as the default when the radio buttons first appear.
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.
To provide a way to select a label for a phone number (such as Home, Work, Mobile, or Other), you can add a spinner to the OrderFragment layout in the DroidCafe app to appear right next to the phone number field.
To add a spinner to the OrderFragment layout in the DroidCafe app, follow these steps:
Spinner element to the bottom of address_text, the right side to the right side of the layout, and the left side to phone_text.Spinner and phone_text elements horizontally, use the pack button
in the toolbar, which provides options for packing or expanding selected UI elements.Spinner and phone_text elements in the Component Tree, click the pack button, and choose Expand Horizontally. As a result, both the Spinner and phone_text elements are set to fixed widths.layout_width drop-down menu, and wrap_content for the layout_height drop-down menu.The layout should look like the figure below. The phone_text element's layout_width drop-down menu in the Attributes pane is set to 172dp. You can optionally experiment with other width settings.
To look at the XML code for fragment_order.xml, click the Code tab.
The Spinner should have the following attributes:
<Spinner
android:id="@+id/label_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/phone_text"
app:layout_constraintTop_toBottomOf="@+id/address_text" />
Be sure to add the android:layout_marginRight and android:layout_marginLeft attributes shown in the code snippet above to maintain compatibility with older versions of Android.
The phone_text element should now have the following attributes (after using the pack tool):
<EditText
android:id="@+id/phone_text"
android:layout_width="172dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:ems="10"
android:hint="@string/enter_phone_hint"
android:inputType="phone"
app:layout_constraintBaseline_toBaselineOf="@+id/phone_label"
app:layout_constraintStart_toEndOf="@+id/phone_label" />
The choices for the Spinner are well-defined static strings such as "Home" and "Work," 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.
strings.xml and 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>
To define the selection callback for the Spinner, change your OrderFragment class to implement the AdapterView.OnItemSelectedListener interface as shown:public class OrderFragment extends Fragment
implements AdapterView.OnItemSelectedListener {
As you type AdapterView. in the statement above, 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. You learn more about this pattern of using an adapter to connect data in another practical. This line should appear in your block of import statements:import android.widget.AdapterView;
After typing OnItemSelectedListener in the statement above, wait a few seconds for a red light bulb to appear in the left margin.onItemSelected() and onNothingSelected() methods, which are required for OnItemSelectedListener, should be highlighted, and the "Insert @Override" option should be selected. Click OK.onItemSelected() and onNothingSelected() callback methods to the bottom of the OrderFragment 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.Spinner in the onCreateView() method using the label_spinner element in the layout, and set its listener (spinner.setOnItemSelectedListener) in the onCreateView() method, as shown in the following code snippet: @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//setHasOptionsMenu(true);
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_order, container, false);
// Create the spinner.
Spinner spinner = v.findViewById(R.id.label_spinner);
if (spinner != null) {
spinner.setOnItemSelectedListener(this);
}
// Create ArrayAdapter using the string array and default spinner layout.
onCreateView() method, add a statement that creates the ArrayAdapter with the string array (labels_array) using the Android-supplied Spinner layout for each item (layout.simple_spinner_item): // Create ArrayAdapter using the string array and default spinner layout.
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(getContext(),
R.array.labels_array, android.R.layout.simple_spinner_item);
// Specify the layout to use when the list of choices appears.
The simple_spinner_item layout used in this step, and the simple_spinner_dropdown_item layout used in the next step, are the default predefined 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 its appearance.Spinner 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);
}
// ... End of Spinner code ...
When the user selects an item in the Spinner, the Spinner 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 fill in the code for the onItemSelected() method to retrieve the selected item in the Spinner, using getItemAtPosition(), and assign the item to the spinnerLabel variable:
onItemSelected() callback method, as shown below, to retrieve the user's selected item using getItemAtPosition(), and assign it to spinnerLabel. You can also add a call to the displayToast() method you already added to OrderFragment:public void onItemSelected(AdapterView<?> adapterView, View view, int
i, long l) {
spinnerLabel = adapterView.getItemAtPosition(i).toString();
displayToast(spinnerLabel);
}
There is no need to add code to the empty onNothingSelected() callback method for this example.Spinner appears next to the phone entry field and shows the first choice (Home). Tapping the Spinner reveals all the choices, as shown in the figure below.
Tapping a choice in the Spinner shows a Toast message with the choice, as shown in the figure below.
Challenge: Write code to perform an action directly from the keyboard by tapping a Send key, such as for dialing a phone number:


android:imeOptions attribute to the phone_text EditText element with the actionSend value:android:imeOptions="actionSend"
The user can now press the Send key to dial the phone number, as shown in the figure above.In the onCreate() method for this Activity, you can use setOnEditorActionListener() to set the listener for the EditText to detect if the key is pressed:
EditText editText = findViewById(R.id.editText_main);
// If view is found, set the listener for editText.
if (editText != null)
editText.setOnEditorActionListener
(new TextView.OnEditorActionListener() {
// Override onEditorAction method...
});
For help setting the listener, see Specify the input method type.
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 handled = false;
if (actionId == EditorInfo.IME_ACTION_SEND) {
dialNumber(v);
handled = true;
}
return handled;
}
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() {
// Find the editText_main view.
EditText editText = v.findViewById(R.id.phone_text);
String phoneNum = null;
// If the editText field is not null,
// concatenate "tel: " with the phone number string.
if (editText != null) phoneNum = "tel:" +
editText.getText().toString();
// Optional: Log the concatenated phone number for dialing.
Log.d(TAG, "dialNumber: " + phoneNum);
// Specify the intent.
Intent intent = new Intent(Intent.ACTION_DIAL);
// Set the data for the intent as the phone number.
intent.setData(Uri.parse(phoneNum));
// If the intent resolves to a package (app),
// start the activity with the intent.
// note that the emulator will not be able to handle this intent
if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
startActivity(intent);
} else {
Log.d("ImplicitIntents", "Can't handle this!");
}
}
The following android:inputType attribute values affect the appearance of the on-screen keyboard:
textAutoCorrect: Suggest spelling corrections.textCapSentences: Start each new sentence with a capital letter.textPersonName: Show a single line of text with suggestions as the user types, and the Done key for the user to tap when they're finished.textMultiLine: Enable multiple lines of text entry and a Return key to add a new line.textPassword: Hide a password when entering it.textEmailAddress: Show an email keyboard rather than a standard keyboard.phone: Show a phone keypad rather than a standard keyboard.android:inputType attribute in the XML layout file for an EditText element To combine values, concatenate them using the pipe (|) character.Radio buttons are input controls that are useful for selecting only one option from a set of options:
RadioButton elements together inside a RadioGroup so that only one RadioButton can be selected at a time.RadioButton elements in the group determines the order that they appear on the screen.android:onClick attribute for each RadioButton to specify the click handler. Alternatively, specify the onClick() handler in the Fragment's onCreateView methodisChecked() method of the Checkable interface, which returns true if the button is selected.Spinner provides a drop-down menu:Spinner to the layout.ArrayAdapter to assign an array of text values as the Spinner menu items.AdapterView.OnItemSelectedListener interface, which requires also adding the onItemSelected() and onNothingSelected() callback methods to activate the Spinner and its listener.onItemSelected() callback method to retrieve the selected item in the Spinner menu using getItemAtPosition().The related concept documentation is in 4.2: Input controls.
Android Studio documentation:
Android developer documentation: