Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to handle onContextItemSelected in a multi fragment activity?

I'm currently trying to adapt my application to use the "Compatibility Libraries for Android v4" to provide the benefits of the usage of fragments even to Android 1.6 users.

The implementation of a context menu seems to be tricky:

  • The main activity of the application is extending the FragmentActivity class.
  • The fragments are all based on one class which extends the Fragment class.
  • The fragment class is calling registerForContextMenu() in its onCreateView() method and overrides the methods onCreateContextMenu() and onContextItemSelected().

For onCreateContextMenu() this works pretty well. The context menu is inflated from a resource file and slightly modified based on the selected item (which is based on a listView... even if the fragment is not an ListFragment).

The problem occurs when a context menu entry is selected. onContextItemSelected() is called for all currently existing fragments starting with the first added one.

In my case the fragments are used to show the content of a folder structure. When the context menu of a subfolder fragment is opened and a menu item is selected, onContextItemSelected() is first called on the upper levels (depending on how many fragments are allowed/visible in this moment).

Right now, I use a workaround by a field on activity level which holds the tag of last fragment calling its onCreateContextMenu(). This way I can call "return super.onContextItemSelected(item)" in the begin of onContextItemSelected() when the stored tag is not the same as getTag(). But this approach looks a bit dirty to me.

Why is onContextItemSelected() called on all fragments? and not just one the one that was calling onCreateContextMenu()?

What is the most elegant way to handle this?

like image 589
Lars K. Avatar asked Mar 14 '11 11:03

Lars K.


3 Answers

I'll post an answer even though you found a workaround because I just dealt with a similar issue. When you inflate the context menu for a specific fragment, assign each menu item a groupId that is unique to the fragment. Then test for the groupId in 'onContextItemSelected.' For Example:

public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
    menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.string.src1);
    menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.string.src2);
}
public boolean onContextItemSelected(MenuItem item) {
    //only this fragment's context menus have group ID of -1
    if (item.getGroupId() == UNIQUE_FRAGMENT_GROUP_ID) {
        switch(item.getItemId()) {
        case MENU_OPTION_1: doSomething(); break;
        case MENU_OPTION_2: doSomethingElse(); break;
    }
}

This way all of your fragments will still receive calls to 'onContextItemSelected,' but only the correct one will respond, thus avoiding the need to write activity-level code. I assume a modified version of this technique could work even though you aren't using 'menu.add(...)'

like image 74
Rich Ehmer Avatar answered Oct 23 '22 04:10

Rich Ehmer


Another one solution:

@Override
public boolean onContextItemSelected(MenuItem item) {
    if (getUserVisibleHint()) {
        // context menu logic
        return true;
    }
    return false;
}

Based upon this patch from Jake Wharton.

like image 55
Sergey Glotov Avatar answered Oct 23 '22 02:10

Sergey Glotov


I liked the simple solution by Sergei G (based on Jake Wharton fix), but inverted because it is easier to add to several fragments:

public boolean onContextItemSelected(android.view.MenuItem item) 
{  
    if( getUserVisibleHint() == false ) 
    {
        return false;
    }

    // The rest of your onConextItemSelect code
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
 }

After that, the code same as it was before.

like image 8
azelez Avatar answered Oct 23 '22 02:10

azelez