Going a la Carte with Your Menu - Creating a Feature‐Rich Application - Android Application Development For Dummies, 3rd Edition (2015)

Android Application Development For Dummies, 3rd Edition (2015)

Part III. Creating a Feature‐Rich Application

Chapter 11. Going a la Carte with Your Menu

In This Chapter

arrow Building an Options menu

arrow Creating a long‐press action

Every good Android application includes menus. If you have an Android device and you’ve downloaded a few applications from the Google Play Store, you’ve probably encountered plenty of menu implementations. You’ll recognize them by their icons or text in the action bar, or their text in the drop‐down overflow menu on the far right of the action bar.

Activities and fragments can both have menus, in which case they’ll both be combined into one. In this chapter, you add option and context menus to the fragments in the Tasks app, but you could just as easily add them to an activity, too.

Understanding Options and Context Menus

Android provides a simple mechanism for you to add menus to your applications. This is the Options menu (also known as the action bar menu). This is, most likely, the most common type of menu that you’ll work with. It’s the primary menu for an activity or fragment.

The Options menu is in the action bar at the top of the screen (read more about the action bar in Chapter 1). Figure 11-1 shows the Options menu with the overflow menu collapsed and expanded.

image

Figure 111: The Options menu with (top) menu icons and (bottom) the overflow menu expanded.

Android 2.x and earlier didn’t have an action bar, so menus on those devices showed up behind a dedicated hardware menu button and were shown at the bottom of the screen in a grid of icons. Most apps won’t need to worry about Android 2.x at this point. If you want to learn more about supporting older versions of Android, see Chapter 17.

You have two choices when making Options menus:

· Show in action bar or put in the overflow menu: Options menu items can either be shown in the action bar, as is the case with the magnifying glass and folder icons in Figure 11-1, or displayed in the overflow menu. Use the action bar for the one or two most important actions that can be taken in your activity. For anything less important, use the overflow menu. It’s important to not overload your action bar with a ton of menu options, both because you have limited real estate, but also because too many options can be overwhelming to the user.

· Icon or Text: For option menus that are shown in the action bar, you have a choice between using an icon or using text. Icons are generally preferable, but in the case where you have only a single action in the action bar, it often makes sense to use text. For example, an app might have a single action called “Save” in the action bar, but you wouldn’t want to have multiple text actions such as “Save” and “Share” because the text can start to get crowded.

Creating Your First Menu

You can create a menu through code or through an XML file that’s provided in the res/menu directory. The preferred method of creating a menu is to define it through XML and then inflate it into a Java object that you can interact with. This helps separate the menu definition from the application code.

Defining the XML file

To define an XML menu, follow these steps:

1. Create a menu folder in the res directory.

2. Add a file by the name of menu_list.xml to the menu directory.

3. Type the following code into the menu_list.xml file:

<?xml version="1.0" encoding="utf-8"?>
<!-- Our default menu, which we'll load into the action bar of our main
app activity -->
<menu xmlns:android="http://schemas.android.com/apk/res/android">

<!-- The Insert button, used to create a new task. This button
is important and small, so we'll force it to always be
in the action bar. In general, you should try to have as
few items as possible configured with showAsAction=always.
We'll use the default Add button icon that ships with
Android. If you want to see what other drawables Android
ships with, you can look in
$ANDROID_SDK/platforms/android-*/res/drawable-*
-->
<item
android:id="@+id/menu_insert"
android:icon="@android:drawable/ic_menu_add"
android:showAsAction="always"
android:title="@string/menu_insert"/>

</menu>

Notice that a new string resource is included (shown in bold). You’ll create that in Step 4. The android:icon value is a built‐in Android icon. The ldpi, mdpi, hdpi, xhdi, and so on, versions of this icon are all built into the Android platform, so you don’t have to provide this bitmap in your drawable resources. To view other available resources, view the android.R.drawable documentation at

http://d.android.com/reference/android/R.drawable.html

All resources in the android.R class (as opposed to your own app’s R class) give your application a common user interface and user experience with the Android platform.

4. Create a new string resource with the name menu_insert with the value of Add Task in the strings.xml resource file.

5. Open the TaskListFragment class and add the following method:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}

setHasOptionsMenu() tells the activity that this fragment has an Options menu to show in the action bar. You place this call in onActivityCreated to be sure that the activity has finished calling its own onCreate before you call setHasOptionsMenu.

6. Add the onCreateOptionsMenu() method to your class:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu,inflater);
inflater.inflate(R.menu.menu_list, menu);
}

The MenuInflater inflates the XML menu created earlier and adds it to the menu that was passed as an argument to the method call.

7. Install the application in the emulator, and click the Menu button.

Figure 11-2 shows the Add Task menu icon that you just created. If you long‐press on the icon, you can see the text “Add Task”.

image

Figure 112: The Add Task menu icon.

Handling user actions

After you’ve created the menu, you then have to add what happens when a user clicks it. To do this, type the following code at the end of the TaskListFragment Java file:

@Override
public boolean onOptionsItemSelected(MenuItem item) { →2
switch (item.getItemId()) { →3
case R.id.menu_insert:
((OnEditTask) getActivity()).editTask(0); →5
return true; →6
}

return super.onOptionsItemSelected(item); →9
}

The lines of code are explained in detail here:

2 This is the method that’s called when a menu item is selected. The item parameter identifies which menu item the user tapped.

3 To determine which item you’re working with, compare the ID of the menu items with the known menu items you have. Therefore, a switch statement is used to check each possible valid case. You obtain the menu’s ID through the MenuItem method ­getItemId().

5 If the user selected the Add Task menu item, the application is instructed to create a task through the editTask() method (defined in Chapter 10). By convention, calling editTask() with an ID of 0 means the app should create a new task.

6 This line returns true to inform the onMenuItemSelected() method that a menu selection was handled.

9 If the menu selection and return isn’t handled earlier, the parent class tries to handle the menu item.

If you run your app, you will now be able to access the edit screen of the app by pressing the Add Task button in the action bar. You won’t actually be able to create a new task, of course, because the app is using hardcoded dummy data for now, but you will be able to after you create your database in Chapter 13.

Creating your second menu

One good thing deserves another. Why stop at one menu when you could have two? In particular, your edit page needs an option to allow users to save their changes.

In this section, you will create a menu programmatically instead of using XML.

Open TaskEditFragment.java and add the following methods and ­constant:

private static final int MENU_SAVE = 1; → 1

@Override
public void onActivityCreated(Bundle savedInstanceState) { → 4
super.onActivityCreated(savedInstanceState);
setHasOptionsMenu(true);
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);

menu.add(0, MENU_SAVE, 0, R.string.confirm) → 13
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); → 14
}

There are some differences from the previous menu:

1 Creates a constant int that will represent the Save menu. Each menu item within an activity should have a unique integer.

4 The onActivityCreated method is identical to the one you added to TaskListFragment in the previous section.

13 Creates a menu item named “Save” and gives it an id of 1. If you have multiple menu items, it’s a good practice to create static final ints to name them.

14 setShowAsAction has several possible values that determine the way that the menu item appears in the action bar:

· ifRoom: Only place this item in the action bar if there is room for it.

· withText: Also include the title text (defined by android:title) with the action item. You can include this value along with one of the others as a flag set, by separating them with a pipe (|).

· never: Never place this item in the action bar.

· always: Always place this item in the action bar. Avoid using this unless it’s critical that the item always appear in the action bar. Setting multiple items to always appear as action items can result in them overlapping with other UI in the action bar. In this case, because the Save option is a critical piece of UI functionality that we never want hidden, it’s okay to use always.

· collapseActionView: The action view associated with this action item (as declared by android:actionLayout or android:actionViewClass) is collapsible. This is a more advanced option used for custom menu items.

You added a new string, so add it to strings.xml:

<string name="confirm">Save</string>

Now, add the following method to handle the menu option when it is selected:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
// The Save button was pressed
case MENU_SAVE: →5
//save(); →6

((OnEditFinished) getActivity()).finishEditingTask(); →8

return true;
}

return super.onOptionsItemSelected(item); →13
}

The onOptionsItemSelected method for TaskEditFragment is similar to the one you added to TaskListFragment, but it has some differences:

5 Was onOptionItemsSelected called with a menu ID of MENU_SAVE? If so, then . . .

6 . call the save method. But really, the save method is commented out for now because the Tasks app is using dummy data and does not yet have a way to save items. That will be added in Chapter 13.

8 Tells the enclosing activity that you are done so that it can clean up whatever it needs to clean up. You will implement this method later in this section.

13 If you can’t handle this menu item, see if your parent can return super.onOptionsItemSelected(item);.

The previous code handles most of the things necessary to implement a Save menu item for the fragment. The one thing that remains is to figure out what to do after the Save button has been clicked. In this case, the activity should close and return the user to the previous activity. Much like you did when you created the OnEditTask interface in Chapter 10, you will create an interface named OnEditFinished that has a method called ­finishEditingTask() that handles this behavior. In Chapter 16, you will make finishEditingTask do something different for tablets, but for now it just needs to close the activity.

Create a new interface named OnEditFinished in com/dummies/tasks/interfaces. Put the following code in it:

public interface OnEditFinished {
/**
* Called when the user finishes editing a task.
*/
public void finishEditingTask();
}

And then implement the interface in TaskEditActivity.java:

public class TaskEditActivity extends Activity
implements OnEditFinished
{
/**
* Called when the user finishes editing a task.
*/
@Override
public void finishEditingTask() {
// When the user dismisses the editor, call finish to destroy
// this activity.
finish();
}

. . .
}

Now run the app; you should see a Save menu overlaying the task image, as in Figure 11-3. If you tap it, the activity should dismiss.

Try setting setAsActionBar to MenuItem.SHOW_AS_ACTION_NEVER to see what the Add Task item would look like in the overflow menu.

image

Figure 113: The Save menu action.

Creating a LongPress Action

The Tasks application needs a mechanism in which to delete a task when it’s no longer needed. Users can long‐press the task in the list, and a dialog appears that allows them to delete the task by selecting an item from the menu. For this section, you’re going to implement a dialog.

Open TaskListAdapter.java and add the following code below your call to setOnClickListener in onBindViewHolder:

// Set the long-press action
viewHolder.cardView.setOnLongClickListener(
new View.OnLongClickListener()
{
@Override
public boolean onLongClick(View view) {
new AlertDialog.Builder(context) →7
.setTitle(R.string.delete_q) →8
.setMessage(viewHolder.titleView.getText()) →9
.setCancelable(true) →10
.setNegativeButton(android.R.string.cancel, null) →11
.setPositiveButton( →12
R.string.delete,
new DialogInterface.OnClickListener() {
@Override
public void onClick(
DialogInterface dialogInterface,
int i)
{
deleteTask(context, i); →20
}
})
.show(); →23
return true;
}
});

7 Uses the AlertDialog.Builder class to construct a new AlertDialog. The Builder has several methods such as ­setTitle, setMessage, setPositiveButton, and so on, that will construct an alert to your specifications. When you’re all done, you will call show on line 23 to show the dialog.

8 Sets the top‐line title of the dialog. Add the following line to your strings.xml:

<string name="delete_q">Delete?</string>

9 Sets the message body of the dialog to the title of the task, so that the user can confirm exactly which item she is deleting.

10 Makes the dialog cancelable. This means that if the user hits the Back button, the dialog behaves as if the user hit the Cancel button. You can set this to false to disable the Back button in dialogs.

11 Sets the text and action for the negative button in the dialog. In this case, the text is Cancel. You don’t need to add this string to your app because the android.R file has it built‐in. Also, you can set the click action to null, because by default the negative button dismisses the dialog, and that’s the only behavior you need for this dialog.

12 Sets the text and action for the positive button in the dialog. The text is Delete, which you should add now to your strings.xml:

<string name="delete">Delete</string>

Unlike the negative button, the positive button has to do some specific stuff to the Tasks app, so you need to implement a DialogInterface.OnClickListener to handle the behavior of a positive button click.

20 When the positive button is clicked, call the method deleteTask. For now, this method does not need to do anything, so add a deleteTask method to your TaskListAdapter like so:

void deleteTask(Context context, long id ) {
Log.d("TaskListAdapter", "Called deleteTask" );
}

23 You must call show() to display your dialog.

Because your viewHolder is being accessed from within an inner class, you also need to change the signature of your method:

public void onBindViewHolder(final ViewHolder viewHolder, final int i)

Now run your app. When you long‐press on a task, you should see a dialog like the one in Figure 11-4:

image

Figure 114: The Delete dialog.

And when you press the Delete button, a message should appear in your logcat output that says Called deleteTask. See Chapter 3 for more information about how to use logcat.

In Chapter 10, you used DialogFragments to create dialogs, but here you are using an AlertDialog (which is not a DialogFragment). What is the difference?

DialogFragments are more complicated to set up, but they interact properly with the Android activity and fragment lifecycle (see Chapter 5 and 9 for more information about Android activities and fragments). In particular, if a fragment is asked to save itself inonSaveInstanceState and then re‐create itself later in onCreate (see Chapter 10 for more information about onSaveInstanceState), a DialogFragment will behave properly but an AlertDialog will not.

You can see this in action by going to the edit page of your app, dismissing the keyboard, and then clicking on the date to open the date picker. If you rotate your device, the date picker remains on the screen and retains whatever date you clicked on.

However, if you long‐press on an item in the list view to open the Delete dialog and then rotate your device, the Delete dialog disappears! This is because you did not use a DialogFragment to create it.

In general, it’s best to always use DialogFragments because they result in the best user experience. As an exercise, try re‐implementing the Delete dialog as a DialogFragment using the lessons described in Chapter 10.