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:
protected void onCreate (Bundle savedInstanceState)
{
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())
{
}
}
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:
="1.0"="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.
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) { }
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:
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.