Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Android Compatibility popup menu with icons (requires support library v7)
/**
* Seems like the only way to use it currently (as of 10/2018) is through reflection
* see https://resocoder.com/2018/02/02/popup-menu-with-icons-android-kotlin-tutorial-code/
**/
package com.vuzz.snapdish.ui;
import android.content.Context;
import android.support.v7.internal.view.SupportMenuInflater;
import android.support.v7.internal.view.menu.MenuBuilder;
import android.support.v7.internal.view.menu.MenuPopupHelper;
import android.support.v7.internal.view.menu.MenuPresenter;
import android.support.v7.internal.view.menu.SubMenuBuilder;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
/**
* Copied from android.support.v7.widget.PopupMenu.
* "mPopup.setForceShowIcon(true);" in the constructor does the trick :)
*
* @author maikvlcek
* @since 5:00 PM - 1/27/14
*/
public class IconizedMenu implements MenuBuilder.Callback, MenuPresenter.Callback {
private Context mContext;
private MenuBuilder mMenu;
private View mAnchor;
private MenuPopupHelper mPopup;
private OnMenuItemClickListener mMenuItemClickListener;
private OnDismissListener mDismissListener;
/**
* Callback interface used to notify the application that the menu has closed.
*/
public interface OnDismissListener {
/**
* Called when the associated menu has been dismissed.
*
* @param menu The PopupMenu that was dismissed.
*/
public void onDismiss(IconizedMenu menu);
}
/**
* Construct a new PopupMenu.
*
* @param context Context for the PopupMenu.
* @param anchor Anchor view for this popup. The popup will appear below the anchor if there
* is room, or above it if there is not.
*/
public IconizedMenu(Context context, View anchor) {
mContext = context;
mMenu = new MenuBuilder(context);
mMenu.setCallback(this);
mAnchor = anchor;
mPopup = new MenuPopupHelper(context, mMenu, anchor);
mPopup.setCallback(this);
mPopup.setForceShowIcon(true);
}
/**
* @return the {@link android.view.Menu} associated with this popup. Populate the returned Menu with
* items before calling {@link #show()}.
*
* @see #show()
* @see #getMenuInflater()
*/
public Menu getMenu() {
return mMenu;
}
/**
* @return a {@link android.view.MenuInflater} that can be used to inflate menu items from XML into the
* menu returned by {@link #getMenu()}.
*
* @see #getMenu()
*/
public MenuInflater getMenuInflater() {
return new SupportMenuInflater(mContext);
}
/**
* Inflate a menu resource into this PopupMenu. This is equivalent to calling
* popupMenu.getMenuInflater().inflate(menuRes, popupMenu.getMenu()).
* @param menuRes Menu resource to inflate
*/
public void inflate(int menuRes) {
getMenuInflater().inflate(menuRes, mMenu);
}
/**
* Show the menu popup anchored to the view specified during construction.
* @see #dismiss()
*/
public void show() {
mPopup.show();
}
/**
* Dismiss the menu popup.
* @see #show()
*/
public void dismiss() {
mPopup.dismiss();
}
/**
* Set a listener that will be notified when the user selects an item from the menu.
*
* @param listener Listener to notify
*/
public void setOnMenuItemClickListener(OnMenuItemClickListener listener) {
mMenuItemClickListener = listener;
}
/**
* Set a listener that will be notified when this menu is dismissed.
*
* @param listener Listener to notify
*/
public void setOnDismissListener(OnDismissListener listener) {
mDismissListener = listener;
}
/**
* @hide
*/
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
if (mMenuItemClickListener != null) {
return mMenuItemClickListener.onMenuItemClick(item);
}
return false;
}
/**
* @hide
*/
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
if (mDismissListener != null) {
mDismissListener.onDismiss(this);
}
}
/**
* @hide
*/
public boolean onOpenSubMenu(MenuBuilder subMenu) {
if (subMenu == null) return false;
if (!subMenu.hasVisibleItems()) {
return true;
}
// Current menu will be dismissed by the normal helper, submenu will be shown in its place.
new MenuPopupHelper(mContext, subMenu, mAnchor).show();
return true;
}
/**
* @hide
*/
public void onCloseSubMenu(SubMenuBuilder menu) {
}
/**
* @hide
*/
public void onMenuModeChange(MenuBuilder menu) {
}
/**
* Interface responsible for receiving menu item click events if the items themselves
* do not have individual item click listeners.
*/
public interface OnMenuItemClickListener {
/**
* This method will be invoked when a menu item is clicked if the item itself did
* not already handle the event.
*
* @param item {@link MenuItem} that was clicked
* @return <code>true</code> if the event was handled, <code>false</code> otherwise.
*/
public boolean onMenuItemClick(MenuItem item);
}
}
@rburgosnavas
Copy link

rburgosnavas commented Sep 9, 2014

This is great, just what I needed! Thank you.

@kaeflint
Copy link

kaeflint commented May 21, 2015

You saved me a lot of hours. Thanks

@dit-j
Copy link

dit-j commented Jun 30, 2015

some Idea how to use it?

@farruhha
Copy link

farruhha commented Jul 27, 2015

but how can i use Menu Builder.CallBacks. When i try to use it, my idea cant recognize it

@kevinkl3
Copy link

kevinkl3 commented Sep 23, 2015

Usage example:

public void showPopup(View v) {
        IconizedMenu popup = new IconizedMenu(this, v);
        MenuInflater inflater = popup.getMenuInflater();
        inflater.inflate(R.menu.menu_myactivity, popup.getMenu());
        popup.show();
}

@briansanchez
Copy link

briansanchez commented Sep 26, 2015

thank you!........

@showrav017
Copy link

showrav017 commented Oct 1, 2015

This only works for static xml Android resource menu. the dynamic menu adding command menu.add("text") will not work here as per my knowledge.

Usage example:

File: R.menu.menu_myactivity

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">
    <item 
        android:id="@+id/action_settings" 
        android:title="@string/action_settings"
        android:icon="@drawable/ic_launcher"
        android:orderInCategory="100" app:showAsAction="never" />
</menu>

Java part

IconizedMenu PopupMenu;
final Button _anchor = (Button) findViewById(R.id.button);

_anchor.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
           showPopUp(_anchor);
      }
});

void showPopUp(View anchorView)
{
    PopupMenu = new IconizedMenu(getWindow().getContext(), anchorView);
    Menu menu = PopupMenu.getMenu();
    MenuInflater inflater = PopupMenu.getMenuInflater();
    inflater.inflate(R.menu.menu_myactivity, PopupMenu.getMenu());
    PopupMenu.show();
}

Thanks mediavrog.

@jjworkshop
Copy link

jjworkshop commented Dec 18, 2015

It's great! Thank you so much.

@iragx
Copy link

iragx commented Jan 23, 2016

@showrav017 sir what are you trying to show? about the code you share? sorry i'm just starting to learn android programming :)

@iragx
Copy link

iragx commented Jan 24, 2016

@mediavrog is it possible to show icons inside popup menu in grid view? i wanted to know to implement? just starting to learn android programming.
2016_01_25_02 09 52

@tedchirvasiu
Copy link

tedchirvasiu commented Feb 16, 2016

Thank you! Works perfectly!
Heads up for those using appcombat v7:23.1.1 and up, they changed the packages slightly.

import android.support.v7.view.SupportMenuInflater;
import android.support.v7.view.menu.MenuBuilder;
import android.support.v7.view.menu.MenuPopupHelper;
import android.support.v7.view.menu.MenuPresenter;
import android.support.v7.view.menu.SubMenuBuilder;

Just remove .internal from there :)

@bansari-hirani
Copy link

bansari-hirani commented Mar 9, 2016

context menu size problem

@mediavrog thank you very much for this awesome gist , it it that what i am looking for it work fine have little bit size issue
i have 8 item in context menu but it shows just only one item
if anyone have slimier issue or have any workaround then let me know
Thank you very much

@chiragdigi
Copy link

chiragdigi commented Apr 1, 2016

How can i use type face for menu text?

@anggadarkprince
Copy link

anggadarkprince commented Apr 13, 2016

I recommend to add
public void setGravity(int gravity) { mPopup.setGravity(gravity); }

considering need to align the popup

@feriyalsusanto
Copy link

feriyalsusanto commented Apr 20, 2016

Hey guys, i got an error like this :

Failed to resolve attribute at index 6

Can anyone help me :) thanks

@nishantapatil
Copy link

nishantapatil commented May 7, 2016

Can we have this menu shown above the anchorView?
I am using this inside a RecyclerView's item and it scrolls the Card just to show this popupMenu.

@ofirmiron
Copy link

ofirmiron commented May 22, 2016

tedchirvasiu thanks!

@abaxxx
Copy link

abaxxx commented Jul 1, 2016

mPopup.setCallback(this); doesn't work since latest AppCompat lib

Copy link

ghost commented Jul 8, 2016

Exactly. Appcompat 24.0.0 mPopup.setCallback can't invoke.

@tuinui
Copy link

tuinui commented Sep 1, 2016

use mPopup.setPresenterCallback(this); instead
@kimcy929
@abaxxx

Copy link

ghost commented Sep 2, 2016

Thank you so much.
@tuinui

@firdousakond
Copy link

firdousakond commented Sep 24, 2016

Thank u so much.. worked like a charm.

@chintanpatel89
Copy link

chintanpatel89 commented Nov 25, 2016

Thank you so much. @tuinui

@LOG-TAG
Copy link

LOG-TAG commented Dec 6, 2016

How to remove the empty space if we are not using the text??

@miankhalid
Copy link

miankhalid commented Jul 7, 2017

Support v7 MenuPopupHelper is now hidden and restricted to LIBRARY_GROUP 😢

More details here:
https://stackoverflow.com/questions/44969577/support-v7-menupopuphelper-is-now-hidden-and-restricted-to-library-group

@debjitk
Copy link

debjitk commented May 16, 2018

@miankhalid Did you found a work around. MenuBuilder and MenuPopupHelper both are now restricted to LIBRARY_GROUP

@mediavrog
Copy link
Author

mediavrog commented Oct 18, 2018

Seems like the only way to use it currently is through reflection https://resocoder.com/2018/02/02/popup-menu-with-icons-android-kotlin-tutorial-code/

@AbandonedCart
Copy link

AbandonedCart commented Nov 12, 2021

Thank you! Works perfectly! Heads up for those using appcombat v7:23.1.1 and up, they changed the packages slightly.

import android.support.v7.view.SupportMenuInflater; import android.support.v7.view.menu.MenuBuilder; import android.support.v7.view.menu.MenuPopupHelper; import android.support.v7.view.menu.MenuPresenter; import android.support.v7.view.menu.SubMenuBuilder;

Just remove .internal from there :)

And now, you replace import android.support.v7 with androidx.appcompat

import androidx.appcompat.view.menu.MenuBuilder;
import androidx.appcompat.view.menu.MenuPopupHelper;
import androidx.appcompat.view.menu.MenuPresenter;
import androidx.appcompat.view.menu.SubMenuBuilder;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment