如何在NavigationView上为MenuItem设置长按监听器?

16

如何在MenuItem上设置长按监听器?

我尝试了这个答案,但该方法对我不起作用。有任何解决办法吗?

代码:

Menu menu = navigationView.getMenu();
MenuItem menuItem = menu.findItem(R.id.menu_item);

// TODO set a long click listener on the menuItem.
menuItem.setOnLongClickListener(...); // Method does not exist, any other solutions?

编辑:我不想设置自定义的ActionView,我希望长按菜单项时整个MenuItem都能有监听器,而无需使用自定义视图。


请展示你的代码。 - BooDoo
@BooDoo,其实没有什么可以展示的,但我添加了一些代码。 - Thomas Vos
1
@BooDoo - 他想在菜单项上进行长按事件,而不是列表视图。 - ceph3us
@SuperThomasLab 你最终得到了一个明确的答案吗?我无法让以下解决方案起作用。谢谢。 - drod
@drod 不好意思,我没能让它正常工作。 - Thomas Vos
显示剩余7条评论
7个回答

8

使用工具栏的众多方法之一(假设我们使用Toolbar)- 这个示例应该让您了解如何在工具栏按钮上实现长按:

class MyActivity extends Activity {    

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /** get menu inflater */
        MenuInflater menuInflater = getMenuInflater();
        /** Inflate the menu
         * this adds items to the action bar if it is present. */
        menuInflater.inflate(R.menu.menu_home, menu);
        /** find interesting item */
        MenuItem item = menu.findItem(R.id.itemId);
        /** set action view */
        item.setActionView(new ImageButton(this)); // this is a Context.class object
        /** set listener  on action view */
        item.getActionView().setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return false;
            }
        });

        return super.onCreateOptionsMenu(menu);
    }
}

在方法 onCreateOptionsMenu - 或者任何你能获取菜单项引用的方法(省略步骤1-2) 中:
  1. 创建菜单,例如通过inflate方式
  2. 获取菜单项
  3. 创建动作视图,例如ImageButton
  4. 在动作视图上设置长按监听器
  5. 在菜单项上设置一个动作视图

以上我设置了一个动作视图,然后从菜单项中获取它并设置监听器(顺序无所谓,也可以这样做):

ImageButton imageButton = new ImageButton(Context);
imageButton.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                return false;
            }
        });
item.setActionView(imageButton);

顺便提一下,您也可以在菜单项上的xml中设置图像视图作为属性:

 <item
    ... 
    android:actionViewClass="android.widget.ImageButton"
 />

then you can get the action view by cast 

   View menuItemActionView = menu.findItem(R.id.itemId).getActionView();
   if(menuItemActionView != null 
            && ImageButton.class.isAssignableFrom(menuItemActionView.getCLass())) {
        ImageButton imageButton = (ImageButton) menuItemActionView;
   }

但是,您仅将长按侦听器设置为操作视图,而不是整个项目。 – SuperThomasLab

-- 否,您是在单个元素上设置操作视图 在这种情况下,您更改了菜单项的默认视图(到 ImageButton 小部件)- 操作视图可以是简单或复杂的视图类型。

但是,如果您不想更改视图,而是保留默认视图呢? – SuperThomasLab

例如 (这是许多方法之一通过使用布局树观察器 / 通过设置布局更改侦听器):

    private View.OnLongClickListener onLongClickListener = new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            return false;
        }
    };


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        /** get menu inflater */
        MenuInflater menuInflater = getMenuInflater();
        /** Inflate the menu
         * this adds items to the action bar if it is present. */
        menuInflater.inflate(R.menu.menu_home, menu);
        /** geta menu item using findItem(int itemid) */
        MenuItem item = menu.findItem(R.id.itemLogOut);
        /** check if we have item */
        if(item!=null) {
            /** try get its action view */
            View actionView = item.getActionView();
             /** check if action view is already set? */
            if(actionView==null) {
                /** get item id  to comparte later in observer listener*/
                final int itemId = item.getItemId();
                /** if not set on top most window an layout changes listener */
                getWindow().getDecorView()
                           .addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
                    @Override
                    public void onLayoutChange(View v, int left, int top, int right, 
                      int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
                           /** try get view by id we have stored few line up */
                           View viewById = v.getRootView().findViewById(itemId);
                           /** check if we have any result */
                           if(viewById!=null) {
                                /** set our listener */                     
                                viewById.setOnLongClickListener(onLongClickListener);
                                /** remove layout observer listener */
                                v.removeOnLayoutChangeListener(this);
                           }
                    }
                });
            } else {
                /** if set we can add our on long click listener */
                actionView.setOnLongClickListener(onLongClickListener);
            }
        }
  } 

我尝试了OnLayoutChangeListener,但它仍然不起作用。没有任何变化。- SuperThomasLab

是的,它可以工作 - 但我知道为什么在你的情况下它不起作用??? - 在我的示例中,我们检查视图项目是否已布局,而不是更改菜单视图是否已布局,然后检查菜单是否包含项目

这是适合你学习的工作代码:

https://github.com/c3ph3us/LongClickOnMenuItem

回复其他评论:

@ SuperThomasLab,对我来说,setOnLongClickListener(...)方法为什么不可用并不明显。- Hossein Seifi

@HosseinSeifi - 看看 android.view.MenuItem 接口 - 它没有提供这样的方法 - 所以对于优秀的程序员来说,这应该是显而易见的 :) 他为什么不能达到实现类方法。


感谢您的回答。但是长按监听器在哪里?我该如何将其设置为“MenuItem”? - Thomas Vos
我尝试了OnLayoutChangeListener,但它仍然不起作用。没有任何变化。 - Thomas Vos
2
我相信这里提供的解决方法是唯一能让它正常工作的方式。我认为你不能在不提供自己的ActionView或者TreeView解决方案的情况下使长按生效。理想情况下,在默认的MenuItem上调用getActionView()并设置一个长按监听器就可以正常工作,但有时候安卓系统真的很烂 :-( - SoftWyer
每个项目需要设置不同的ID。 - EmmanuelMess
非常好的回答。您是否知道是否有一种方法(使用您不修改默认菜单项视图的方法)来强制长按以防止调用onOptionsItemSelected? - snapfractalpop
显示剩余4条评论

4

解决方案

这并不完美,但仍然比大家在这里建议的更好,因为它适用于整个项目布局,而不仅仅是actionview的一部分。

注意:

  1. 我只在MaterialComponents library 1.1.0-alpha08中测试过,所以我不能保证它将来会起作用
  2. 我已经要求开发人员提供更好的API,但他们关闭了这个问题 ‍♂️
  3. index是一个项目的位置,它还包含HeaderLayout、Divider、Category(子菜单标题)的位置

好吧,这就是代码:

val navigationView = findViewById(R.id.navigation_view)
val navigationRecycler = navigationView[0] as RecyclerView // core-ktx extension for ViewGroup
val navigationLayoutManager = navigationRecycler.layoutManager as LinearLayoutManager
val navigationAdapter = navigationRecycler.adapter

if (navigationAdapter != null) {
    navigationRecycler.post { // this line is important
        for (index in 0 until navigationAdapter.itemCount) {
            val item = navigationLayoutManager.findViewByPosition(index)
            item?.setOnLongClickListener {
                Toast.makeText(this, "finally!", Toast.LENGTH_SHORT).show()
                true
            }
        }
    }
}

但我认为它必须是“在0..navigationAdapter.itemCount中”,否则最后一项将被忽略。 - Cyb3rKo

1
你可以这样做:

        val homeTab = bottomNavigationView.findViewById<BottomNavigationItemView>(R.id.navigation_home) 
        //R.id.navigation_home id of navigation menu items you provided in menu file for BottomNavigationView

        homeTab.setOnLongClickListener {
              Toast.makeText(this@MainActivity,"Home Long clicked!",Toast.LENGTH_LONG).show()
              true
    }

0

来自massivemadness answer,这里有一个针对菜单的轻微变化。我也不认为这是稳定的,感觉像是一种补救措施,但可以将其嵌套在try catch中,保持冷静并继续前进 :)

if(navigationAdapter != null) {
            navigationRecycler.post { // this line is important
                for(index in 0 until navigationAdapter.itemCount) {
                    val item = navigationLayoutManager.findViewByPosition(index)
                    item?.setOnLongClickListener {
                        try{
                            Toast.makeText(this, "${nav_view.menu[index - 1 /*Number of children before your menu*/].title}!", Toast.LENGTH_SHORT).show()
                            true
                        } catch (e:Throwable){
                            false
                        }


                    }
                }
            }
        }


0
您可以通过以下方式实现:
action_menu.xml
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:support="http://schemas.android.com/apk/res-auto" >

<item
    android:id="@+id/item1"
    support:showAsAction="always">
</item>

custom_action_view.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="10dp"
    android:paddingRight="5dp" >

<ImageButton
    android:id="@+id/customActionItem"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerVertical="true"
    android:background="@drawable/abc_item_background_holo_dark"
    android:src="@drawable/bulb_icon" />

</RelativeLayout>

菜单填充器的代码如下:

public boolean onCreateOptionsMenu(Menu menu) {
    // TODO Auto-generated method stub
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.action_menu, menu);     

    final MenuItem item1= menu.findItem(R.id.item1);
    MenuItemCompat.setActionView(item1, R.layout.custom_action_view);
    View vItem1= MenuItemCompat.getActionView(item1);

    final ImageButton customActionItem= (ImageButton) vItem1.findViewById(R.id.customActionItem);
    customActionItem.setOnLongClickListener(new OnLongClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            // do something here
        }
    });

    return super.onCreateOptionsMenu(menu);
}

这与其他答案相同。我不想要自定义的ActionView。我想将长按监听器设置为普通项目,而不使用自定义视图。 - Thomas Vos

0
我已在下方添加了长按导航视图的功能。我想获取视图中支持项的长按事件。
步骤:
1. 在我的活动中实现了 DrawerLayout.DrawerListener 接口。 2. 将我的活动设置为 DrawerLayout 的侦听器。
DrawerLayout drawer = findViewById(R.id.drawer_layout);
drawer.addDrawerListener(this);

实现了onDrawerOpened方法,在其中将长按设置为该项。

@Override
public void onDrawerOpened(@NonNull View drawerView) {
    View support = findViewById(R.id.nav_support);
    support.setOnLongClickListener(view -> {
        // TODO: Handle the long press here.
        return false;
    });
}

在这个解决方案中的主要思路是获得MenuItemView,并在View上设置setOnLongClickListener。我将其添加到onDrawerOpened方法中,因为此时我确定View已创建。我之前尝试过早获取视图,但它不可用,findViewById方法返回null

Result


0
mBottomNavigationView.findViewById(menuItemId).setOnLongClickListener(new OnLongClickListener() {
    @Override
    public boolean onLongClick(View v) {
        return true;
    }
});

4
这可能解决问题,但您可能希望添加为什么您的解决方案有效的原因。一点解释可以帮助很多。 - Tavo
这个答案对我有用。您能否解释一下为什么它有效? - Kristian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接