Search code examples
androidexpandablelistviewandroid-contextmenu

Long-Click Triggers both Listview's Context Menu and Selection on Android 7 Device


I have an ExpandableListView for which I implemented selection (short-click) and deletion (long-click). Short list item clicks are handled by onChildClick(), long clicks are handled by onCreateContextMenu().

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
    mDeleteItemGroup = ExpandableListView.getPackedPositionGroup(info.packedPosition);
    mDeleteItemChild = ExpandableListView.getPackedPositionChild(info.packedPosition);

    menu.setHeaderTitle("some title");
    MenuInflater inflater = mActivity.getMenuInflater();
    inflater.inflate(R.menu.menu_my_view_context, menu);
}

Above shows the context menu code, which handled the long click well. Problem was the lack of styleability, it truncating longer titles on some devices. So I used a custom dialog box instead of the standard context menu as follows:

@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);

    ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
    mDeleteItemGroup = ExpandableListView.getPackedPositionGroup(info.packedPosition);
    mDeleteItemChild = ExpandableListView.getPackedPositionChild(info.packedPosition);

    String title = "some title";

    ConfirmDeletePopupFragment confirmDeletePopupFragment = ConfirmDeletePopupFragment.newInstance(title);
    confirmDeletePopupFragment.setTargetFragment(this, 0);
    confirmDeletePopupFragment.show(getActivity().getSupportFragmentManager(), "tag");
}

This works nicely on all devices except a Nexus 5X running Android 7. Here, a long click triggers both the context menu AND the selection via onChildClick, which is obviously not what I want.

How can I prevent the item selection while still using my custom dialog.


Solution

  • I can offer my current solution or workaround that feels sub-optimal as it patches up something I broke by replacing the context menu with my custom dialog. Idea is to mute the selection handling when the deletion handling is started and unmute it in the dialog's callback.

    This works but I'd prefer not breaking it in the first place. So there is probably a better way.

    public class MyListFragment extends ExpandableListFragment implements ConfirmDeletePopupFragment.DialogListener {
    
        (...)   
    
        private boolean mMuteSelection = false;
    
        (...)
    
        @Override
        public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
            super.onCreateContextMenu(menu, v, menuInfo);
    
            mMuteSelection = true;
    
            ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
            mDeleteItemGroup = ExpandableListView.getPackedPositionGroup(info.packedPosition);
            mDeleteItemChild = ExpandableListView.getPackedPositionChild(info.packedPosition);
    
            String title = "some title"
    
            ConfirmDeletePopupFragment confirmDeleteWeakPopupFragment = ConfirmDeletePopupFragment.newInstance(title);
            confirmDeletePopupFragment.setTargetFragment(this, 0);
            confirmDeletePopupFragment.show(getActivity().getSupportFragmentManager(), "tag");
        }
    
        (...)
    
        @Override
        public boolean onChildClick(ExpandableListView arg0, View arg1, int group, int child, long arg4) {
            super.onChildClick(arg0, arg1, group, child, arg4);
    
            if (!mMuteSelection) {
                (handle selection)
            }
            return false;
        }
    
        (...)
    
        @Override
        public void onDeleteConfirm(boolean delete) {
            if (delete) {
                (handle deletion)
            }
            mMuteSelection = false;
        }
    }