Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/215.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 带有图标的弹出菜单_Android_Popupmenu - Fatal编程技术网

Android 带有图标的弹出菜单

Android 带有图标的弹出菜单,android,popupmenu,Android,Popupmenu,当然,我们这里讨论的是SDK 11及以上版本 我打算做类似的事情: 在该弹出菜单中的每个项旁边,我想放置一个图标 我创建了一个XML文件并将其放在/菜单中: <menu xmlns:android="http://schemas.android.com/apk/res/android" > <item android:id="@+id/action_one" android:title="Sync" android:i

当然,我们这里讨论的是SDK 11及以上版本

我打算做类似的事情:

在该
弹出菜单中的每个项旁边,我想放置一个图标

我创建了一个
XML
文件并将其放在
/菜单中

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:id="@+id/action_one"
        android:title="Sync"
        android:icon="@android:drawable/ic_popup_sync"
        />

    <item
        android:id="@+id/action_two"
        android:title="About"
        android:icon="@android:drawable/ic_dialog_info"
        />
</menu>


正如您所注意到的,在xml文件中,我定义了我想要的图标,但是,当弹出菜单显示时,它显示的是没有图标的图标。我应该怎么做才能使这两个图标出现?

否则我会实现它:

创建
PopUpWindow
布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/llSortChangePopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/sort_popup_background"
android:orientation="vertical" >

<TextView
    android:id="@+id/tvDistance"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/distance"
    android:layout_weight="1.0"
    android:layout_marginLeft="20dp"
    android:paddingTop="5dp"
    android:gravity="center_vertical"
    android:textColor="@color/my_darker_gray" />

<ImageView
    android:layout_marginLeft="11dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/sort_popup_devider" 
    android:contentDescription="@drawable/sort_popup_devider"/>

<TextView
    android:id="@+id/tvPriority"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/priority"
    android:layout_weight="1.0"
    android:layout_marginLeft="20dp"
    android:gravity="center_vertical"
    android:clickable="true"
    android:onClick="popupSortOnClick"
    android:textColor="@color/my_black" />


<ImageView
    android:layout_marginLeft="11dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/sort_popup_devider" 
    android:contentDescription="@drawable/sort_popup_devider"/>

<TextView
    android:id="@+id/tvTime"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/time"
    android:layout_weight="1.0"
    android:layout_marginLeft="20dp"
    android:gravity="center_vertical"
    android:clickable="true"
    android:onClick="popupSortOnClick"
    android:textColor="@color/my_black" />

<ImageView
    android:layout_marginLeft="11dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/sort_popup_devider" 
    android:contentDescription="@drawable/sort_popup_devider"/>

<TextView
    android:id="@+id/tvStatus"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/status"
    android:layout_weight="1.0"
    android:layout_marginLeft="20dp"
    android:gravity="center_vertical"
    android:textColor="@color/my_black" 
    android:clickable="true"
    android:onClick="popupSortOnClick"
    android:paddingBottom="10dp"/>

 </LinearLayout>
最后,单击按钮或其他任何东西,将其弹出:

 imTaskStatusButton.setOnClickListener(new OnClickListener() 
        {
            public void onClick(View v) 
            {
                 int[] location = new int[2];
                 currentRowId = position;
                 currentRow = v;    
                 // Get the x, y location and store it in the location[] array
                 // location[0] = x, location[1] = y.
                 v.getLocationOnScreen(location);

                 //Initialize the Point with x, and y positions
                 point = new Point();
                 point.x = location[0];
                 point.y = location[1];
                 showStatusPopup(TasksListActivity.this, point);
            }
        });
PopUpWindow
的一个好例子:


我用最简单的方法解决了我的问题,从未想过会如此简单:

在main.xml中:

<menu xmlns:android="http://schemas.android.com/apk/res/android" >

<item
    android:id="@+id/action_more"
    android:icon="@android:drawable/ic_menu_more"
    android:orderInCategory="1"
    android:showAsAction="always"
    android:title="More">
    <menu>
        <item
            android:id="@+id/action_one"
            android:icon="@android:drawable/ic_popup_sync"
            android:title="Sync"/>
        <item
            android:id="@+id/action_two"
            android:icon="@android:drawable/ic_dialog_info"
            android:title="About"/>
    </menu>
</item>

这是一个技巧,使用子菜单读取弹出菜单源代码。我们可以通过以下代码显示图标:

Field field = popupMenu.getClass().getDeclaredField("mPopup");
field.setAccessible(true);
MenuPopupHelper menuPopupHelper = (MenuPopupHelper) field.get(popupMenu);
menuPopupHelper.setForceShowIcon(true);
但是MenuPopupHelper.java在android内部包中。因此,我们应该使用反射:

    PopupMenu popupMenu = new PopupMenu(this, anchor);
    popupMenu.getMenuInflater().inflate(R.menu.process, popupMenu.getMenu());

    try {
        Field field = popupMenu.getClass().getDeclaredField("mPopup");
        field.setAccessible(true);
        Object menuPopupHelper = field.get(popupMenu);
        Class<?> cls = Class.forName("com.android.internal.view.menu.MenuPopupHelper");
        Method method = cls.getDeclaredMethod("setForceShowIcon", new Class[]{boolean.class});
        method.setAccessible(true);
        method.invoke(menuPopupHelper, new Object[]{true});
    } catch (Exception e) {
        e.printStackTrace();
    }

    popupMenu.show();
PopupMenu-PopupMenu=新的PopupMenu(这个,锚定);
充气(R.menu.process,popupMenu.getMenu());
试一试{
Field Field=popupMenu.getClass().getDeclaredField(“mPopup”);
字段。setAccessible(true);
Object menuPopupHelper=field.get(弹出菜单);
Class cls=Class.forName(“com.android.internal.view.menu.MenuPopupHelper”);
方法Method=cls.getDeclaredMethod(“setForceShowIcon”,新类[]{boolean.Class});
方法setAccessible(true);
调用(menuPopupHelper,新对象[]{true});
}捕获(例外e){
e、 printStackTrace();
}
show();

如果你想在弹出菜单中显示图标,请看一看,它非常酷而且易于使用

Android弹出菜单有一个隐藏的方法来显示菜单图标。使用Java反射来启用它,如下代码片段所示

public static void setForceShowIcon(PopupMenu popupMenu) {
    try {
        Field[] fields = popupMenu.getClass().getDeclaredFields();
        for (Field field : fields) {
            if ("mPopup".equals(field.getName())) {
                field.setAccessible(true);
                Object menuPopupHelper = field.get(popupMenu);
                Class<?> classPopupHelper = Class.forName(menuPopupHelper
                        .getClass().getName());
                Method setForceIcons = classPopupHelper.getMethod(
                        "setForceShowIcon", boolean.class);
                setForceIcons.invoke(menuPopupHelper, true);
                break;
            }
        }
    } catch (Throwable e) {
        e.printStackTrace();
    }
}
public static void setforceshowcon(弹出菜单弹出菜单){
试一试{
Field[]fields=popupMenu.getClass().getDeclaredFields();
用于(字段:字段){
if(“mPopup.equals(field.getName())){
字段。setAccessible(true);
Object menuPopupHelper=field.get(弹出菜单);
Class CLASSPOPUSCURLPER=Class.forName(menuPopupHelper
.getClass().getName());
方法setForceIcons=classPopucUpCelper.getMethod(
“setForceShowIcon”,boolean.class);
调用(menuPopupHelper,true);
打破
}
}
}捕获(可丢弃的e){
e、 printStackTrace();
}
}

如果您使用的是AppCompat v7,则此方法有效。它有点黑,但比使用反射要好得多,并且让您仍然可以使用Android的核心弹出菜单:

res/menu/popup.xml



这将导致使用菜单资源文件中定义的图标弹出菜单:


我在尝试@Stephen Kidson的答案和@david.schereiber的建议,我意识到在
MenuBuilder
中没有这样的方法
setOnMenuItemClickListener
。对v7的源代码进行了一点修改,我找到了以下解决方案:

        MenuBuilder menuBuilder = new MenuBuilder(mContext);
        new SupportMenuInflater(mContext).inflate(R.menu.my_menu, menuBuilder);
        menuBuilder.setCallback(new MenuBuilder.Callback() {
            @Override
            public boolean onMenuItemSelected(MenuBuilder menu, MenuItem menuItem) {
                // your "setOnMenuItemClickListener" code goes here
                switch (menuItem.getItemId()) {
                    case R.id.menu_id1:
                        // do something 1
                        return true;

                    case R.id.menu_id2:
                        // do something 2
                        return true;
                }
                return false;
            }

            @Override
            public void onMenuModeChange(MenuBuilder menu) {
            }
        });
        MenuPopupHelper menuHelper = new MenuPopupHelper(mContext, menuBuilder, v);
        menuHelper.setForceShowIcon(true); // show icons!!!!!!!!
        menuHelper.show();

使用
MenuBuilder
MenuPopupHelper

    MenuBuilder menuBuilder =new MenuBuilder(this);
    MenuInflater inflater = new MenuInflater(this);
    inflater.inflate(R.menu.menu, menuBuilder);
    MenuPopupHelper optionsMenu = new MenuPopupHelper(this, menuBuilder, view);
    optionsMenu.setForceShowIcon(true);

    // Set Item Click Listener
    menuBuilder.setCallback(new MenuBuilder.Callback() {
        @Override
        public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
            switch (item.getItemId()) {
                case R.id.opt1: // Handle option1 Click
                    return true;
                case R.id.opt2: // Handle option2 Click
                    return true;
                default:
                    return false;
            }
        }

        @Override
        public void onMenuModeChange(MenuBuilder menu) {}
    });


    // Display the menu
    optionsMenu.show();
menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/opt1"
        android:icon="@mipmap/ic_launcher"
        android:title="option 1" />
    <item
        android:id="@+id/opt2"
        android:icon="@mipmap/ic_launcher"
        android:title="option 2" />
</menu>


基于@Ajay-answer…以下是我所做的

 @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.add_task, menu);  // for the two icons in action bar
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {

            case R.id.menu:
                View menuItemView = findViewById(R.id.menu);
                MenuBuilder menuBuilder =new MenuBuilder(this);
                MenuInflater inflater = new MenuInflater(this);
                inflater.inflate(R.menu.popup, menuBuilder);
                MenuPopupHelper optionsMenu = new MenuPopupHelper(this, menuBuilder, menuItemView); 
                optionsMenu.setForceShowIcon(true);
                optionsMenu.show();

            default:
                return super.onOptionsItemSelected(item);
        }
    }
弹出窗口

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:id="@+id/opt1"
    android:icon="@drawable/change_pic"
    android:title="Change Picture" />
<item
    android:id="@+id/opt2"
    android:icon="@drawable/change_pin"
    android:title="Change Password" />

    <item
        android:id="@+id/opt3"
        android:icon="@drawable/sign_out"
        android:title="Sign Out" />
</menu>

屏幕截图


AppCompat中的
菜单opupHelper
类具有
@hide
注释。如果这是一个问题,或者您出于任何原因无法使用AppCompat,那么在
菜单项
标题中使用一个
Spannable
,它包含图标和标题文本

主要步骤是:

  • 使用
    菜单
    xml文件为
    弹出菜单
    充气
  • 如果任何项目有图标,则对所有项目执行此操作:
    • 如果项目没有图标,请创建一个透明图标。这将确保没有图标的项目与带有图标的项目对齐
    • 创建一个包含图标和标题的
      SpannableStringBuilder
    • 将菜单项的标题设置为
      SpannableStringBuilder
    • 将菜单项的图标设置为null,“以防万一”
优点:没有反思。不使用任何隐藏的API。可以使用框架弹出菜单

缺点:更多代码。如果你有一个没有图标的子菜单,它会在一个小屏幕上留下不需要的空白


详情:

首先,在
dimens.xml
文件中定义图标的大小:

<dimen name="menu_item_icon_size">24dp</dimen>
截图:

在/res/menu目录中列出\u item\u menu.xml

<?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

            <item
                android:id="@+id/locale"
                android:title="Localizar"
                android:icon="@mipmap/ic_en_farmacia_ico"
                app:showAsAction="always">
            </item>

            <item android:id="@+id/delete"
                android:title="Eliminar"
                android:icon="@mipmap/ic_eliminar_ico"
                app:showAsAction="always">
            </item>
    </menu>
结果


如果您不熟悉反射,您可以通过使用反射来实现这一点。借助这一出色的java高级功能,您可以修改JVM中运行的应用程序的运行时行为。您可以查看对象并在运行时执行其方法。在本例中,我们需要在运行时修改弹出菜单行为,而不是扩展核心类并修改它;)希望能有所帮助

private void showPopupMenu(View view) {
    // inflate menu
    PopupMenu popup = new PopupMenu(mcontext, view);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.main, popup.getMenu());

    Object menuHelper;
    Class[] argTypes;
    try {
        Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
        fMenuHelper.setAccessible(true);
        menuHelper = fMenuHelper.get(popup);
        argTypes = new Class[]{boolean.class};
        menuHelper.getClass().getDeclaredMethod("setForceShowIcon", argTypes).invoke(menuHelper, true);
    } catch (Exception e) {

    }
    popup.show();




} 

谢谢埃米尔的回复。然而,我发现了一种方法,用一个名为setforceicon.(true)或类似的方法覆盖当前的PopupMenu类。。我忘了不,我没弄明白,我在问你是否已经知道这样的事情。。。我还在寻找解决办法!哈哈,如果我没有弄错,当你定义菜单XML时,你想让菜单在按下硬件菜单按钮时弹出,对吗?不,我想让它在按下角落里的
视图时弹出,就像上面显示的“谷歌地图”应用程序一样。它现在正在工作吗?当你按下设计中的视图时,它会弹出吗
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
    android:id="@+id/opt1"
    android:icon="@drawable/change_pic"
    android:title="Change Picture" />
<item
    android:id="@+id/opt2"
    android:icon="@drawable/change_pin"
    android:title="Change Password" />

    <item
        android:id="@+id/opt3"
        android:icon="@drawable/sign_out"
        android:title="Sign Out" />
</menu>
<dimen name="menu_item_icon_size">24dp</dimen>
/**
 * Moves icons from the PopupMenu's MenuItems' icon fields into the menu title as a Spannable with the icon and title text.
 */
public static void insertMenuItemIcons(Context context, PopupMenu popupMenu) {
    Menu menu = popupMenu.getMenu();
    if (hasIcon(menu)) {
        for (int i = 0; i < menu.size(); i++) {
            insertMenuItemIcon(context, menu.getItem(i));
        }
    }
}

/**
 * @return true if the menu has at least one MenuItem with an icon.
 */
private static boolean hasIcon(Menu menu) {
    for (int i = 0; i < menu.size(); i++) {
        if (menu.getItem(i).getIcon() != null) return true;
    }
    return false;
}

/**
 * Converts the given MenuItem's title into a Spannable containing both its icon and title.
 */
private static void insertMenuItemIcon(Context context, MenuItem menuItem) {
    Drawable icon = menuItem.getIcon();

    // If there's no icon, we insert a transparent one to keep the title aligned with the items
    // which do have icons.
    if (icon == null) icon = new ColorDrawable(Color.TRANSPARENT);

    int iconSize = context.getResources().getDimensionPixelSize(R.dimen.menu_item_icon_size);
    icon.setBounds(0, 0, iconSize, iconSize);
    ImageSpan imageSpan = new ImageSpan(icon);

    // Add a space placeholder for the icon, before the title.
    SpannableStringBuilder ssb = new SpannableStringBuilder("       " + menuItem.getTitle());

    // Replace the space placeholder with the icon.
    ssb.setSpan(imageSpan, 1, 2, 0);
    menuItem.setTitle(ssb);
    // Set the icon to null just in case, on some weird devices, they've customized Android to display
    // the icon in the menu... we don't want two icons to appear.
    menuItem.setIcon(null);
}
PopupMenu popupMenu = new PopupMenu(view.getContext(), view);
popupMenu.inflate(R.menu.popup_menu);
insertMenuItemIcons(textView.getContext(), popupMenu);
popupMenu.show();
<?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

            <item
                android:id="@+id/locale"
                android:title="Localizar"
                android:icon="@mipmap/ic_en_farmacia_ico"
                app:showAsAction="always">
            </item>

            <item android:id="@+id/delete"
                android:title="Eliminar"
                android:icon="@mipmap/ic_eliminar_ico"
                app:showAsAction="always">
            </item>
    </menu>
private void showPopupOption(View v){
    PopupMenu popup = new PopupMenu(getContext(), v);
    popup.getMenuInflater().inflate(R.menu.list_item_menu, popup.getMenu());

    popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
        public boolean onMenuItemClick(MenuItem menu_item) {
            switch (menu_item.getItemId()) {
                case R.id.locale:
                    break;
                case R.id.delete:
                    break;
            }
            return true;
        }
    });

    MenuPopupHelper menuHelper = new MenuPopupHelper(getContext(), (MenuBuilder) popup.getMenu(), v);
    menuHelper.setForceShowIcon(true);
    menuHelper.setGravity(Gravity.END);
    menuHelper.show();
}
private void showPopupMenu(View view) {
    // inflate menu
    PopupMenu popup = new PopupMenu(mcontext, view);
    MenuInflater inflater = popup.getMenuInflater();
    inflater.inflate(R.menu.main, popup.getMenu());

    Object menuHelper;
    Class[] argTypes;
    try {
        Field fMenuHelper = PopupMenu.class.getDeclaredField("mPopup");
        fMenuHelper.setAccessible(true);
        menuHelper = fMenuHelper.get(popup);
        argTypes = new Class[]{boolean.class};
        menuHelper.getClass().getDeclaredMethod("setForceShowIcon", argTypes).invoke(menuHelper, true);
    } catch (Exception e) {

    }
    popup.show();




}