如何向导航抽屉项目添加长按功能?

Posted

技术标签:

【中文标题】如何向导航抽屉项目添加长按功能?【英文标题】:How to add long press functionality to navigation drawer items? 【发布时间】:2016-12-25 21:48:56 【问题描述】:

正如标题所说,我正在尝试向我的应用导航抽屉中的项目添加长按功能。这些项目是动态添加的(不是从navigation_drawer_menu.xml 膨胀的),所以我无法通过在xml 文件中指定一些属性来解决这个问题。

我查看了有关 *** 的几个问题,尤其是这个问题:How to set a long click listener on a MenuItem (on a NavigationView)?。我已经实现了 setActionView 解决方案,但最终在导航抽屉项目的右边缘得到了一个空白按钮。当我长按文本时,没有任何反应。当我长按小空白按钮时,我得到了我想要的。

如何为整个 menuItem 设置 OnLongClickListener,而不仅仅是在其右侧(我假设它是一个按钮)?感谢您的阅读,如果需要更多信息,我很乐意帮助您:)

【问题讨论】:

您找到解决方案了吗?我有同样的问题。 我没有...我只是决定使用按钮右侧的图标并使其可点击。 【参考方案1】:

我遇到了同样的问题,并设法通过挖掘 NavigationView 的视图层次结构来解决它。

第一步是了解 NavigationView 的视图层次结构。您可以使用this post 中的代码打印出 NavigationView 的视图层次结构。

然后开始挖掘您的目标视图。就我而言:

@Override
public boolean onPrepareOptionsMenu(Menu menu) 

    // Start digging into the view hierarchy until the correct view is found
    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
    ViewGroup navigationMenuView = (ViewGroup)navigationView.getChildAt(0);
    ViewGroup navigationMenuItemView = (ViewGroup)navigationMenuView.getChildAt(2);
    View appCompatCheckedTextView = navigationMenuItemView.getChildAt(0);

    // Attach click listener
    appCompatCheckedTextView.setOnLongClickListener(new View.OnLongClickListener() 
        @Override
        public boolean onLongClick(View v) 
            Log.i("test", "LONG CLICK");
            return true;
        
    );

    return super.onPrepareOptionsMenu(menu);

【讨论】:

【参考方案2】:

我们选择做这些事情不是因为它们容易,而是因为它们很难。而且因为如果我不能这样做,我的 UI 就会脱胶。

将下面给出的NavigationItemLongPressInterceptor 类导入您的项目。

NavigationView 的菜单项被声明为正常,具有两个附加属性以添加长按行为。

<menu xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:showIn="navigation_view">

    <group android:id="@+id/home_views">
            <item
                android:id="@+id/nav_item_1"
                android:icon="@drawable/ic_item_1"
                android:title="Item 1"
                android:checkable="true"


                app:actionViewClass=
                   "com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
                app:showAsAction="always"

                />
            <item
                android:id="@+id/nav_item_2"
                android:icon="@drawable/ic_item_2"
                android:title="Item 2"
                android:checkable="true"


                app:actionViewClass=
                   "com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
                app:showAsAction="always"

                />
    </group>
</menu>

为您的活动添加 NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener 的实现, 并实现onNavigationItemLongClick 方法:

public class MainActivity extends MediaActivity
        implements
            NavigationView.OnNavigationItemSelectedListener,
            NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener
    . . .
    @Override
    public void onNavigationItemLongClick(
        NavigationItemLongPressInterceptor.SelectedItem selectedItem,
        View view)
     
        // supply your NavigationView as an argument.
        int menItemId = selectedItem.getItemId(mNavigationView);
        switch (id) 
            ...
            case R.id.nav_local_device:
            case R.id.nav_upnp_devices: 
                showNavigationItemSetAsHomePopupMenu(id,view);

            
            break;
        

    


您可能必须将 com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor 添加到您的 proguard 规则中。

NavigationItemLongPressInterceptor.java:

package com.twoplay.netplayer.controls;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.util.AttributeSet;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;

import com.google.android.material.navigation.NavigationView;

import androidx.annotation.Nullable;

/**
 * Attach a long-click handler to a menu item in a NavigationView menu.
 *
 * To handle long-click of a Navigator menu item, declare the item as normal, and append
 * app:actionViewClass="com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor" and
 * app:showAsAction="always" attributes:
 *
 * <menu xmlns:tools="http://schemas.android.com/tools"
 *     xmlns:app="http://schemas.android.com/apk/res-auto"
 *     >
 *
 *     <group android:id="@+id/home_views">
 *             <item
 *                 android:id="@+id/nav_item_1"
 *                 android:icon="@drawable/ic_item_1"
 *                 android:title="Item 1"
 *                 android:checkable="true"
 *                  app:actionViewClass=
 *                    "com.twoplay.netplayer.controls.NavigationItemLongPressInterceptor"
 *                 app:showAsAction="always"
 *             </item>
 *      </group>
 *
 *  Your Application class must implement <L NavigationItemLongPressInterceptor.OnNavigationItemLongClickListener/>
 *  in order to receive notification of long pressed menu items.
 *
 *  You can retrieve the item id of the menu by calling <L SelectedItem.getItemId/> on the
 *  <L SelectedItem/> provided as an argument to <L NavigationItemLongPressInterceptor.onNavigationItemLongClick/>
 *
 *                 />
 *
 */
@SuppressWarnings("unused")
public class NavigationItemLongPressInterceptor extends View 
    public NavigationItemLongPressInterceptor(Context context) 
        super(context);
        init();
    



    public NavigationItemLongPressInterceptor(Context context, @Nullable AttributeSet attrs) 
        super(context, attrs);
        init();
    

    public NavigationItemLongPressInterceptor(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        init();
    

    private void init()
    

        setVisibility(View.INVISIBLE);
        setLayoutParams(new ViewGroup.LayoutParams(0,0));
    
    public interface OnNavigationItemLongClickListener 
        void onNavigationItemLongClick(SelectedItem itemHandle, View view);
    


    public static class SelectedItem 
        private final View actionView;

        private SelectedItem(View actionView) 
            this.actionView = actionView;
        

        public int getItemId(NavigationView navigationView)
        
            return getItemId(navigationView.getMenu());
        

        private int getItemId(Menu menu) 
            for (int i = 0; i < menu.size(); ++i) 
                MenuItem item = menu.getItem(i);
                if (item.getActionView() == actionView) 
                    return item.getItemId();
                
                SubMenu subMenu = item.getSubMenu();
                if (subMenu != null) 
                    int itemId = getItemId(subMenu);
                    if (itemId != -1) 
                        return itemId;
                    
                
            
            return -1;
        
    

    @Override
    protected void onAttachedToWindow() 
        super.onAttachedToWindow();
        View parent = getMenuItemParent();
        parent.setOnLongClickListener(new View.OnLongClickListener() 
            @Override
            public boolean onLongClick(View v) 
                OnNavigationItemLongClickListener receiver = getReceiver();
                if (receiver == null)
                
                    throw new RuntimeException("Your main activity must implement NavigationViewLongPressInterceptorView.OnNavigationItemLongClickListener");
                
                View parent = getMenuItemParent();
                receiver.onNavigationItemLongClick(
                        new SelectedItem(NavigationItemLongPressInterceptor.this),parent);
                return true;
            
        );
    

    private Activity getActivity() 
        Context context = getContext();
        while (context instanceof ContextWrapper) 
            if (context instanceof Activity) 
                return (Activity)context;
            
            context = ((ContextWrapper)context).getBaseContext();
        
        return null;
    
    private OnNavigationItemLongClickListener getReceiver() 
        Activity activity = getActivity();
        if (activity == null) return null;
        if (activity instanceof OnNavigationItemLongClickListener)
        
            return (OnNavigationItemLongClickListener)activity;
        
        return null;
    

    private View getMenuItemParent() 
        View parent = (View)getParent();
        while (true)
        
            if (parent.isClickable())
            
                return parent;

            
            parent = (View) parent.getParent();
        
    

此代码针对 androidx,但可以轻松地向后移植到 AppCompat。只需删除 androidx 导入,并将它们替换为相应的 AppCompat 导入。希望旧版本的NavigationView 以同样的方式布局actionViews。

使用“androidx.appcompat:appcompat:1.0.2”、“com.google.android.material:material:1.0.0”测试。我有理由相信它是版本安全的。让我知道getMenuItemParent()是否需要针对其他 AppCompat 版本进行调整,我将在此处合并更改。

【讨论】:

感谢您的回答。即使我现在不需要它,但我尝试过它并且效果很好。【参考方案3】:

我不得不做类似的事情并选择 onItemLongClick

https://developer.android.com/reference/android/widget/AdapterView.OnItemLongClickListener.html

我的实现有点不同,因为我在导航抽屉中有一个可扩展列表,并且每个项目都必须有一个 onClick 和一个 onLongClick 方法调用,导航抽屉上的每个项目也是由用户动态添加的应用程序。

 listView.setOnItemLongClickListener(new View.OnItemLongClickListener() 
        @Override
        public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) 

         Toast.makeText(HomeActivity.this, id +"", Toast.LENGTH_LONG).show();

            return true;

        

    );

【讨论】:

以上是关于如何向导航抽屉项目添加长按功能?的主要内容,如果未能解决你的问题,请参考以下文章

如何在新的导航抽屉架构组件中实现共享应用或评分应用或注销功能

在导航抽屉项目单击时,导航抽屉中的新列表视图

如何将图标添加到导航抽屉中的项目

Android开发之Navigationdrawer导航抽屉功能的实现(源码分享)

如何在导航抽屉中添加活动

如何在一个 android studio 项目中为两种不同类型的用户提供两种不同的导航抽屉活动?