Android theme not applied when calling openContextMenu

72 views Asked by At

I have a recyclerview where I want to open the context menu for some items on a single click. I do this by calling openContextMenu()

viewHolder.itemView.setOnClickListener(v -> activity.openContextMenu(viewHolder.itemView));

I register the view for a context menu using setOnCreateContextMenuListener in my bind method.

However, the styling on the context menu is different than when it is opened via a long press: openContextMenu vs. long press

The long press version appears to correctly apply the Theme.MaterialComponents theme. Using View.showContextMenu() produces the same results as Activity.openContextMenu(). Even View.performLongClick() does the same. The theme is being applied in some way, because when I add elements like android:itemBackground to the theme, it applies to both versions of the menu. I haven't figured out an explanation or fix, but will post here if / when I do.

1

There are 1 answers

0
Tad On

The lack of theming of the dialog does seem like a bug, but I now have a better understanding of the cause and a workaround:

The material version of the context menu is designed to pop up from the point where the user clicks on the view. That information isn't present when calling View.showContextMenu(), Activity.openContextMenu() or the default version of View.performLongClick(). Using View.performLongClick(float x, float y) instead does pass the information, resulting in the material version of the dialog. Unfortunately, that method is only available on API 24 and up.

A (rather painful) version that behaves as expected for newer versions of Android:

GestureDetectorCompat detector = new GestureDetectorCompat(fa, new ClickListener(viewHolder.itemView));
viewHolder.itemView.setOnTouchListener((v, event) -> detector.onTouchEvent(event));

With a gesture listener:

private static class ClickListener extends GestureDetector.SimpleOnGestureListener {
    public View view;

    public ClickListener(View view) {
        this.view = view;
    }

    @Override
    public boolean onSingleTapConfirmed(MotionEvent event) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return view.performLongClick(event.getX(), event.getY());
        }
        return view.performLongClick();
    }

    @Override
    public void onLongPress(MotionEvent event) {
        onSingleTapConfirmed(event);
    }
}

Seems like there should be a simpler way, so share if you find one!