调用 openContextMenu 时未应用 Android 主题

问题描述

我有一个 recyclerview,我想通过单击打开某些项目的上下文菜单。我通过调用 openContextMenu()

来做到这一点
viewHolder.itemView.setonClickListener(v -> activity.openContextMenu(viewHolder.itemView));

我在 setonCreateContextMenuListener 方法中使用 bind 为上下文菜单注册视图。

但是,上下文菜单上的样式与通过长按打开时的样式不同:

openContextMenu

long press

长按版本似乎正确应用了 Theme.MaterialComponents 主题。使用 View.showContextMenu() 产生与 Activity.openContextMenu() 相同的结果。甚至 View.performlongClick() 也是如此。主题正在以某种方式应用,因为当我向主题添加 android:itemBackground 等元素时,它适用于菜单的两个版本。我还没有想出解释或修复,但如果/当我这样做时会在这里发布。

解决方法

对话框缺乏主题似乎是一个错误,但我现在对原因和解决方法有了更好的理解:

上下文菜单的材质版本旨在从用户单击视图的位置弹出。调用 View.showContextMenu()Activity.openContextMenu()View.performLongClick() 的默认版本时不存在该信息。改用 View.performLongClick(float x,float y) 确实会传递信息,从而生成对话框的材质版本。不幸的是,该方法仅适用于 API 24 及更高版本。

一个(相当痛苦的)版本,对于较新版本的 Android 来说表现如预期:

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

使用手势监听器:

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);
    }
}

似乎应该有一种更简单的方法,所以如果你找到了,请分享!