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
.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
.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 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: