PopupMenu with icons [duplicate]
Asked Answered
G

12

76

Of course we are dealing here with SDK 11 and above.

I intend to do something similar to this: enter image description here

Next to each item in that PopupMenu, I would like to place an icon.

I created an XML file and placed it in /menu:

<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>

As you noticed, in the xml file I am defining the icons I want, however, when the popup menu shows, it is showing them without the icons. What should I do to make those 2 icons appear?

Germinative answered 16/3, 2013 at 21:38 Comment(1)
I think that's the easiest way to implement such feature: https://mcmap.net/q/266645/-custom-menu-on-button-click-as-drop-downRenee
M
46

I would implement it otherwise:

Create a PopUpWindow layout:

<?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>

and also create the PopUpWindow in your Activity:

    // The method that displays the popup.
private void showStatusPopup(final Activity context, Point p) {

   // Inflate the popup_layout.xml
   LinearLayout viewGroup = (LinearLayout) context.findViewById(R.id.llStatusChangePopup);
   LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   View layout = layoutInflater.inflate(R.layout.status_popup_layout, null);

   // Creating the PopupWindow
   changeStatusPopUp = new PopupWindow(context);
   changeStatusPopUp.setContentView(layout);
   changeStatusPopUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
   changeStatusPopUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
   changeStatusPopUp.setFocusable(true);

   // Some offset to align the popup a bit to the left, and a bit down, relative to button's position.
   int OFFSET_X = -20;
   int OFFSET_Y = 50;

   //Clear the default translucent background
   changeStatusPopUp.setBackgroundDrawable(new BitmapDrawable());

   // Displaying the popup at the specified location, + offsets.
   changeStatusPopUp.showAtLocation(layout, Gravity.NO_GRAVITY, p.x + OFFSET_X, p.y + OFFSET_Y);
}

finally pop it up onClick of a button or anything else:

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

A Good example for PopUpWindow:

http://androidresearch.wordpress.com/2012/05/06/how-to-create-popups-in-android/

Majolica answered 16/3, 2013 at 21:49 Comment(24)
thanks Emil for your reply. However, I've found a way once to override the current PopupMenu class, with a method called setforceicon..(true) or something like that.. I forgotGerminative
No i didn't figure it out, i'm asking you if you already know something like that... I'm still searching for a solution! hahaGerminative
if I am not mistaken when you define the menu XML you want the menu to pop up on the press of the hardware menu button right?Majolica
No I want it to popup on the press of a View in the corner, just like the "Google Maps" app shown above.Germinative
and is it working right now? does it pop up when you press the view in the desired location?Majolica
Yes it popups! but the thing is that it doesn't show the icons. And yes it popups in the desired location under my View. I just want the icons to show that's all :)Germinative
then try this link: #3000366Majolica
Unfortunately, it's not what i'm searching for...Germinative
maybe what you are looking for is not in the menu object, for what I know you can set only a limited number of icons to the menu items and I have no idea how it's control in in the later versions.Have you searched in the documentation? : developer.android.com/guide/topics/ui/menus.htmlMajolica
Don't you think that the one in the pic is a spinner and not a popupmenu?Germinative
actually I think it's a pop that design as I showed you... as a PopUpWindow... but maybe I'm wrong and there is another way to do that.Majolica
Thanks for your time, I do appreciate your help! :)Germinative
You welcome, @JonathanHugh. Hope I was helpful ; )Majolica
What's currentRowId = position; and currentRow = v; here??Scullery
@DroidLearner, you can ignore it in this case, it's not relevant to the Popup example.Majolica
What are R.id.llStatusChangePopup and R.layout.status_popup_layout?Lidalidah
R.layout.status_popup_layout is the popup layout. llStatusChangePopup is the layout in which i pop the popup.Majolica
This approach worked perfectly for me. Saved a ton of time.Stormy
@EmilAdz : what is "@drawable/sort_popup_background" ?Reahard
@young_08, sort_popup_background is just a background resource image for the popup.Majolica
@EmilAdz i as well used the above answer but i couldn't able to inflate layout . can you please help meReahard
@EmilAdz https://mcmap.net/q/266647/-popmenus-with-iconsReahard
How to add on click listeners to each of the text view? Every time I add it I get a null object reference errorRambort
It seems to me like while PopupMenu does dismiss when clicking outside of it PopupWindow behaves differently. How do I get it to behave the same way?Easily
S
112

This way works if you're using AppCompat v7. It's a little hacky but significantly better than using reflection and lets you still use the core Android PopupMenu:

PopupMenu menu = new PopupMenu(getContext(), overflowImageView);
menu.inflate(R.menu.popup);
menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { ... });

MenuPopupHelper menuHelper = new MenuPopupHelper(getContext(), (MenuBuilder) menu.getMenu(), overflowImageView);
menuHelper.setForceShowIcon(true);
menuHelper.show();

res/menu/popup.xml

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

    <item android:id="@+id/menu_share_location"
        android:title="@string/share_location"
        android:icon="@drawable/ic_share_black_24dp"/>

</menu>


This results in the popup menu using the icon that is defined in your menu resource file:

enter image description here

Servomechanism answered 18/8, 2016 at 20:26 Comment(13)
Where you put the icon in this example?Malines
I've updated the example with a sample popup.xml and a screenshotServomechanism
It's quite simple and working like Champ, Good work. Works in app API version as well.Errhine
Great solution! Small tip: You don't even need to create a PopupMenu instance as you did. Just create a new MenuBuilder() inflate the menu into it and pass it to the helper.Washer
Good catch @Washer - I pulled this from my code where I removed some lines. I actually create the PopupMenu so I can use setonMenuItemClickListener(). Going to add this to the example as I'm sure most developers would want to know when their menu item is clicked!Servomechanism
Android Studio is asking me for this: @SuppressLint("RestrictedApi")Revareval
Anyway, i cant find the need class for that. It's throwing this exception: java.lang.ClassCastException: com.android.internal.view.menu.MenuBuilder cannot be cast to android.support.v7.view.menu.MenuBuilderRevareval
@Revareval Use the MenuBuilder from the v7 support library. So change your imports.Rounce
@Revareval To fix the ClassCastException, replace import android.widget.PopupMenu; with import android.support.v7.widget.PopupMenu;Beeeater
Sounded promising but I couldn't get this to work myself - went down the rabbit hole for 45 mins and failed - then I ended up using ListPopupWindow which worked fineDonyadoodad
This also works if you have migrated to the androidx support libraries: androidx.appcompat.widget.PopupMenu.Pliant
A similar solution is actually suggested in material.io/components/menus/… however the warning is that this API is restricted and might not work in the future.Disembody
import androidx.appcompat.widget.PopupMenu;Ganger
M
46

I would implement it otherwise:

Create a PopUpWindow layout:

<?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>

and also create the PopUpWindow in your Activity:

    // The method that displays the popup.
private void showStatusPopup(final Activity context, Point p) {

   // Inflate the popup_layout.xml
   LinearLayout viewGroup = (LinearLayout) context.findViewById(R.id.llStatusChangePopup);
   LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   View layout = layoutInflater.inflate(R.layout.status_popup_layout, null);

   // Creating the PopupWindow
   changeStatusPopUp = new PopupWindow(context);
   changeStatusPopUp.setContentView(layout);
   changeStatusPopUp.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
   changeStatusPopUp.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
   changeStatusPopUp.setFocusable(true);

   // Some offset to align the popup a bit to the left, and a bit down, relative to button's position.
   int OFFSET_X = -20;
   int OFFSET_Y = 50;

   //Clear the default translucent background
   changeStatusPopUp.setBackgroundDrawable(new BitmapDrawable());

   // Displaying the popup at the specified location, + offsets.
   changeStatusPopUp.showAtLocation(layout, Gravity.NO_GRAVITY, p.x + OFFSET_X, p.y + OFFSET_Y);
}

finally pop it up onClick of a button or anything else:

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

A Good example for PopUpWindow:

http://androidresearch.wordpress.com/2012/05/06/how-to-create-popups-in-android/

Majolica answered 16/3, 2013 at 21:49 Comment(24)
thanks Emil for your reply. However, I've found a way once to override the current PopupMenu class, with a method called setforceicon..(true) or something like that.. I forgotGerminative
No i didn't figure it out, i'm asking you if you already know something like that... I'm still searching for a solution! hahaGerminative
if I am not mistaken when you define the menu XML you want the menu to pop up on the press of the hardware menu button right?Majolica
No I want it to popup on the press of a View in the corner, just like the "Google Maps" app shown above.Germinative
and is it working right now? does it pop up when you press the view in the desired location?Majolica
Yes it popups! but the thing is that it doesn't show the icons. And yes it popups in the desired location under my View. I just want the icons to show that's all :)Germinative
then try this link: #3000366Majolica
Unfortunately, it's not what i'm searching for...Germinative
maybe what you are looking for is not in the menu object, for what I know you can set only a limited number of icons to the menu items and I have no idea how it's control in in the later versions.Have you searched in the documentation? : developer.android.com/guide/topics/ui/menus.htmlMajolica
Don't you think that the one in the pic is a spinner and not a popupmenu?Germinative
actually I think it's a pop that design as I showed you... as a PopUpWindow... but maybe I'm wrong and there is another way to do that.Majolica
Thanks for your time, I do appreciate your help! :)Germinative
You welcome, @JonathanHugh. Hope I was helpful ; )Majolica
What's currentRowId = position; and currentRow = v; here??Scullery
@DroidLearner, you can ignore it in this case, it's not relevant to the Popup example.Majolica
What are R.id.llStatusChangePopup and R.layout.status_popup_layout?Lidalidah
R.layout.status_popup_layout is the popup layout. llStatusChangePopup is the layout in which i pop the popup.Majolica
This approach worked perfectly for me. Saved a ton of time.Stormy
@EmilAdz : what is "@drawable/sort_popup_background" ?Reahard
@young_08, sort_popup_background is just a background resource image for the popup.Majolica
@EmilAdz i as well used the above answer but i couldn't able to inflate layout . can you please help meReahard
@EmilAdz https://mcmap.net/q/266647/-popmenus-with-iconsReahard
How to add on click listeners to each of the text view? Every time I add it I get a null object reference errorRambort
It seems to me like while PopupMenu does dismiss when clicking outside of it PopupWindow behaves differently. How do I get it to behave the same way?Easily
C
30

Popup menu with icon using MenuBuilder and 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>

enter image description here

Cropland answered 28/11, 2016 at 8:1 Comment(7)
I want to select option1 and do something, how to achieve?Doxology
@JohnJoe I have added the code sample to handle the menu click.Cropland
this code show error MenuBuilder constructor can only be called from within the same library group (groupId=com.android.support) for MenuBuilder. Same kind of error shown for MenuPopupHelper and setForceShowIconComplaisance
@Aashish This solution worked for me. I don't know why you're getting such an error. Do an error specific search that may help. I will try to figure out it. If you get the solution please share that here, that may help others.Cropland
Aashish: add @SuppressLint("RestrictedApi") above the method, to get rid of the error.Neurogram
it was awesome and it works correctly , just remember to add @SuppressLint("RestrictedApi")at the start of your method . ThanksPrinceling
Took me a while to get to this solution, the only previous thing that worked was accessing the forceShowIcon method via reflection which was not pretty at all. Thanks for the solution!Schmeltzer
M
29

Android popup menu has a hidden method to show menu icon. Use Java reflection to enable it as below code snippet.

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();
    }
}
Mallen answered 30/9, 2015 at 9:49 Comment(4)
For some reason this snippet does not work after I create production apk, although perfectly works in debug mode. It was a disappointing surprise for me :( . Do you have any clue of how to make it work?Trisoctahedron
Probably, proguard changed class/field name. You should both class name and field name for PopupMenu class.Mallen
This solution doesnt change the width of the menu. it doesnt consider the additions of icons so the text is cropped.Handbill
Works perfect with android.support.v7.widget.PopupMenuSolley
B
29

The MenuPopupHelper class in AppCompat has the @hide annotation. If that's a concern, or if you can't use AppCompat for whatever reason, there's another solution using a Spannable in the MenuItem title which contains both the icon and the title text.

The main steps are:

  • inflate your PopupMenu with a menu xml file
  • if any of the items have an icon, then do this for all of the items:
    • if the item doesn't have an icon, create a transparent icon. This ensures items without icons will be aligned with items with icons
    • create a SpannableStringBuilder containing the icon and title
    • set the menu item's title to the SpannableStringBuilder
    • set the menu item's icon to null, "just in case"

Pros: No reflection. Doesn't use any hidden apis. Can work with the framework PopupMenu.

Cons: More code. If you have a submenu without an icon, it will have unwanted left padding on a small screen.


Details:

First, define a size for the icon in a dimens.xml file:

<dimen name="menu_item_icon_size">24dp</dimen>

Then, some methods to move the icons defined in xml into the titles:

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

Finally, create your PopupMenu and use the above methods before showing it:

PopupMenu popupMenu = new PopupMenu(view.getContext(), view);
popupMenu.inflate(R.menu.popup_menu);
insertMenuItemIcons(textView.getContext(), popupMenu);
popupMenu.show();

Screenshot: screenshot

Benito answered 15/1, 2017 at 21:28 Comment(1)
Thanks for providing a solution for non-AppCompat projects. All other answers (on other questions too) don't even mention AppCompat being a requirement.Rounce
C
17

You can implement this By the use of Reflection if u don`t familiar with it with the help of this awesome java advanced feature u can modify the runtime behavior of applications running in the JVM you can look at the object and perform its methods at runtime and in our case we need to modify popupMenu behavior at runtime instead of extend the core class and modify it ;) hope that help

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




} 
Coeducation answered 17/9, 2017 at 15:43 Comment(6)
This worked for me, Nothing else here did. ThanksOneway
not working on every deviceDashtilut
which device you test against @DashtilutCoeducation
The Snippet u have shared is using reflection to access non public apis, These private methods may differ for different vendors, thus this code won't run on every device, i tested on a lenovo device, (dont know it model) , I have to use custom layout with popupwindow .Dashtilut
this way is working @ all my online apps on play store without single crash ..i tested it on Lenovo Tab 10 Tablet and it works very well .. by the way thanks for your noticeCoeducation
so nice solution . thanks ;)Eastwards
M
14

list_item_menu.xml in /res/menu directory

<?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>

In my activity

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

result

popup_menu

Mulford answered 1/4, 2017 at 4:42 Comment(3)
Android Studio is asking me for this: @SuppressLint("RestrictedApi")Revareval
popup.getMenu() not always an instance of MenuBuilder. Thus crash.Frunze
@HanWhiteking Make sure you are using androidx.appcompat.widget.PopupMenu and not android.widget.PopupMenuShermy
W
10

Read the PopupMenu source code. We can show icon by the below code:

Field field = popupMenu.getClass().getDeclaredField("mPopup");
field.setAccessible(true);
MenuPopupHelper menuPopupHelper = (MenuPopupHelper) field.get(popupMenu);
menuPopupHelper.setForceShowIcon(true);

But MenuPopupHelper.java is in android internal package. So we should use Reflection:

    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();
Winnifredwinning answered 4/6, 2015 at 14:18 Comment(3)
Thanks - while this method works if you use Alex's original xml menu file where he has defined the items with their associated icons, I'm concerned about the lack of control that I have been given with regards to setting the margin / padding of the drawable as those options cannot be set. I'm beginning to think using popupmenus is not such a good idea.Coworker
Now you can use MenuPopupHelper in v7 package to do this without reflect.Christiansen
Accessing internal APIs via reflection is not supported and may not work on all devices or in the future warning shown. Any update to resolve it??Complaisance
G
6

I solved my issue the simplest possible way ever, never expected such a simplicity:

In 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>

in MainActivity.java

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

That was a trick by using a submenu

Germinative answered 16/3, 2013 at 23:8 Comment(6)
This is not a solution. The icons does not show..Tram
Works like a charm. You can also use reflection and invoke setForceShowIcon(true);Outcurve
this answer's not related to a PopUpMenu. We all know submenus support icons :)Nightspot
This just creates a nested submenuPrecious
@Germinative this answer is for option menu not for pop menu....how we can use it in pop up menu ?Lessen
This is for ActionBar Options menu, and not applicable to popup menusMuskogean
M
5

If you want to display icon in popup menu, have a look at https://github.com/shehabic/Droppy , it's pretty cool and easy to use

Mccown answered 25/8, 2015 at 9:46 Comment(5)
Without using any library...Is it possible to set icons with titles in pop up menu ?Lessen
@DHAKAD Yes you can as people in this thread discussed. Or you can look at this library source to see how they can set icons and use exactly what you want.Mccown
Actually I try to add icons simply like android:icon="@drawable/ic_menu_more" , why it doesn't work ??Lessen
i don't try this yet https://mcmap.net/q/266648/-is-it-possible-to-display-icons-in-a-popupmenu. And why don't you just use the Droppy lib above, it's simpleMccown
This is the only method that works, and is also upgrade safe. Also, the option of generating the menu programmatic is much neater than generating the menu with layouts and xml. Many thanksSidestep
D
2

Based on @Ajay answer...here is what I did

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

popup

<?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>

ScreenShot

enter image description here

Doxology answered 14/12, 2016 at 7:2 Comment(0)
G
0

I was trying @Stephen Kidson's answer and @david.schereiber's suggestion, and I realized that there is no such method setOnMenuItemClickListener in MenuBuilder. Messed around with the v7's source code a little bit and I found this solution:

        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();
Gearhart answered 7/11, 2016 at 9:22 Comment(2)
There's a more straightforward way using the PopupMenu object my answer above instantiates: menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { ... });Servomechanism
I don't want to create PopupMenu just to inflate the menu then to get it out, with my method I can just inflate the menu directly into the MenuBuilder, I think it's betterGearhart

© 2022 - 2024 — McMap. All rights reserved.