Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Adding a Context Menu to a DialogPreference

0.00/5 (No votes)
4 Apr 2013CPOL2 min read 12.3K  
The Android SDK class DialogPreference doesn't support context menus out of the box. Here's a quick tip on how to implement them.

Introduction

The preferences API provides Android applications with a standardised look and feel for editing common kinds of application settings, like strings, numeric data, boolean options, and so on. But some applications require more complex interfaces and that is where the standard DialogPreference class comes in, allowing you to add your own dialog to the preferences interface and therefore gather any kind of data you want. Unfortunately, DialogPreference doesn't offer any easily-accessible way of displaying a context menu, but for some user interfaces a context menu is really necessary.

In this article, I present a simple way of adding a context menu to DialogPreference, in the context of a preference to edit a list of strings.   I assume the reader is already familiar with the operation of the preferences API as a whole, and therefore skim over matters like the method used to store or retrieve the  list of strings, and show only the code relevant to the matter at hand. 

Background

The usual method for displaying a context menu is to call registerForContextMenu(View) on your Activity object, and then override the Activity's onCreateContextMenu(ContextMenu, View, ContextMenu.ContextMenuInfo) method to provide the necessary options for the menu. A simple example can be seen below:

C#
protected void onCreate (Bundle savedInstanceState)
{
    // initialise content view here
    registerForContextMenu (findViewById (R.id.my_list));
}

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) 
{
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.context_menu, menu);
}

public boolean onContextItemSelected(MenuItem item) 
{
    AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
    switch (item.getItemId()) 
    {
        // handle menu items here
    }
}

Unfortunately, as there is no readily available Activity object in the context of a DialogPreference, this isn't really possible in this situation. 

The solution

To solve the problem, it is necessary to determine yourself when to show the context menu (e.g., in response to a long press, or as I have done here, in response to selecting an item in a list), and then generate the context menu yourself.

My menu has two options, one to delete an item, and another to move it to the top of the list (the item at the top of the list in my application has a special status as a default selection, so you want to be able to change it easily). My context menu file therefore looks like this:

XML
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:id="@+id/delete" android:title="Delete" />
    <item android:id="@+id/makeDefault" android:title="Make default" />
</menu>

In a final application, the titles of the items should ideally be string value references, not hardcoded, of course. 

To display this menu, I'll need an object that implements the OnCreateContextMenuListener interface. Because I like keeping the number of classes I create to a minimum, I'll also implement the other interface I'll need later - OnMenuItemClickListener - in the same object.

Java
public class ContextMenuGenerator implements OnCreateContextMenuListener, OnMenuItemClickListener
{
    private int position;
    private ArrayAdapter<String> adapter;

    public ContextMenuGenerator (int position, ArrayAdapter<String> adapter)
    {
	this.position = position;
	this.adapter = adapter;
    }

    public void onCreateContextMenu (ContextMenu menu, View v, ContextMenuInfo menuInfo)
    {
        new MenuInflater(context).inflate (R.menu.string_list_preference_popup, menu);
        for (int i = 0; i < menu.size (); i ++)
            menu.getItem (i).setOnMenuItemClickListener (this);
    }

    public boolean onMenuItemClick (MenuItem item) { /* we'll define this later */ }

In order to show the menu, we need to specify that this listener is used for the view it's associated with, in my case a ListView in the dialog. Unfortunately, specifying this when the Dialog is created causes some problems with scroll tracking, so we have to do it just prior to displaying the context menu. Once we have done this, we can show the menu, then remove the listener:

Java
private AdapterView.OnItemClickListener showItemMenu = new AdapterView.OnItemClickListener () 
{
    public void onItemClick (AdapterView<?> list, View control, int position, long itemId)
    {
        list.setOnCreateContextMenuListener (new ContextMenuGenerator(position, adapter));
        list.showContextMenuForChild (control);
        list.setOnCreateContextMenuListener (null);
    }
};

This just leaves handling the menu selections, which I leave as an exercise for the reader. 

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)