当然,我们正在处理SDK 11及更高版本。
我打算做类似的事情:
在PopupMenu
中的每个项旁边,我想放置图标。
我创建了一个XML
文件并将其放在/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>
正如您所注意到的,在xml文件中我定义了我想要的图标,但是,当弹出菜单显示时,它显示的是没有图标的图标。如何显示这两个图标?
答案 0 :(得分:75)
这种方式适用于您使用AppCompat v7。它有点hacky,但显着比使用反射更好,并让你仍然使用核心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 /菜单/ 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>
这会导致弹出菜单使用菜单资源文件中定义的图标:
答案 1 :(得分:36)
否则我会实施它:
创建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>
然后在PopUpWindow
中创建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);
}
最后弹出一个按钮或其他任何内容的onClick
:
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
的好例子:
http://androidresearch.wordpress.com/2012/05/06/how-to-create-popups-in-android/
答案 2 :(得分:21)
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();
}
}
答案 3 :(得分:13)
带有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>
答案 4 :(得分:12)
AppCompat中的MenuPopupHelper
类具有@hide
注释。如果这是一个问题,或者由于某种原因你不能使用AppCompat,那么在Spannable
标题中使用MenuItem
的另一个解决方案包含图标和标题文本。
主要步骤是:
PopupMenu
xml文件menu
充气
SpannableStringBuilder
SpannableStringBuilder
优点:没有反思。不使用任何隐藏的api。可以使用框架PopupMenu。
缺点:更多代码。如果你有一个没有图标的子菜单,它会在小屏幕上显示不需要的左边距。
详细说明:
首先,在dimens.xml
文件中定义图标的大小:
<dimen name="menu_item_icon_size">24dp</dimen>
然后,将xml中定义的图标移动到标题中的一些方法:
/**
* 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 popupMenu = new PopupMenu(view.getContext(), view);
popupMenu.inflate(R.menu.popup_menu);
insertMenuItemIcons(textView.getContext(), popupMenu);
popupMenu.show();
答案 5 :(得分:8)
阅读PopupMenu源代码。我们可以通过以下代码显示图标:
Field field = popupMenu.getClass().getDeclaredField("mPopup");
field.setAccessible(true);
MenuPopupHelper menuPopupHelper = (MenuPopupHelper) field.get(popupMenu);
menuPopupHelper.setForceShowIcon(true);
但是MenuPopupHelper.java是在android内部包中。所以我们应该使用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();
答案 6 :(得分:7)
list_item_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>
在我的活动中
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();
}
结果
答案 7 :(得分:6)
你可以实现这个通过使用Reflection如果你不熟悉它,借助这个非常棒的java高级功能你可以修改在JVM中运行的应用程序的运行时行为,你可以查看对象并执行它运行时的方法,在我们的例子中,我们需要在运行时修改popupMenu行为,而不是扩展核心类并修改它;)希望帮助
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();
}
答案 8 :(得分:5)
我用尽可能简单的方式解决了我的问题,从来没有想到这么简单:
在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>
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;
}
这是一个使用子菜单
的技巧答案 9 :(得分:1)
基于@Ajay回答......这就是我做的事情
@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>
<强>截图强>
答案 10 :(得分:0)
我正在尝试@Stephen Kidson的回答和@ david.schereiber的建议,我意识到setOnMenuItemClickListener
中没有这样的方法MenuBuilder
。稍微讨论了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();