我非常喜欢我们在3.0中获得的新PopupMenu,但我无法在菜单项旁边显示任何图标。我正在从下面的.xml中充实菜单:
<item android:id="@+id/menu_delete_product"
android:icon="@drawable/sym_action_add"
android:title="delete"
android:showAsAction="ifRoom|withText" />
<item android:id="@+id/menu_modify_product"
android:icon="@drawable/sym_action_add"
android:title="modify"
android:showAsAction="ifRoom|withText" />
<item android:id="@+id/menu_product_details"
android:icon="@drawable/sym_action_add"
android:title="details"
android:showAsAction="ifRoom|withText" />
使用此代码:
image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu pop = new PopupMenu(getActivity(), v);
pop.getMenuInflater().inflate(R.menu.shelves_details_menu, pop.getMenu());
pop.show();
}
});
我无法显示图标,我错过了什么?
答案 0 :(得分:67)
对Gaelan Bolger提供的解决方案的贡献。 如果您收到&#34; IllegalAccessException,则使用此代码:不允许访问字段&#34;。
PopupMenu popup = new PopupMenu(mContext, view);
try {
Field[] fields = popup.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popup);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
prepareMenu(popup.getMenu());
popup.show();
文本
答案 1 :(得分:28)
如果您愿意冒险,请查看Google的PopupMenu源代码。创建自己的类,即MyPopupMenu,与Google的PopupMenu类相同,但稍作修改。
在PopupMenu的构造函数中:
public MyPopupMenu(Context context, View anchor) {
// TODO Theme?
mContext = context;
mMenu = new MenuBuilder(context);
mMenu.setCallback(this);
mAnchor = anchor;
mPopup = new MenuPopupHelper(context, mMenu, anchor);
mPopup.setCallback(this);
mPopup.setForceShowIcon(true); //ADD THIS LINE
}
使用方法setForceShowIcon强制它显示图标。您也可以根据需要公开一个公共方法来设置此标志。
答案 2 :(得分:18)
我能够使用反射显示图标。它可能不是最优雅的解决方案,但它有效。
try {
Class<?> classPopupMenu = Class.forName(popupMenu
.getClass().getName());
Field mPopup = classPopupMenu.getDeclaredField("mPopup");
mPopup.setAccessible(true);
Object menuPopupHelper = mPopup.get(popupMenu);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
} catch (Exception e) {
e.printStackTrace();
}
答案 3 :(得分:16)
我们可以使用子菜单模型。因此,我们不需要编写显示弹出菜单的方法,它将自动显示。看看:
menu.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>
</menu>
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;
}
结果是:
答案 4 :(得分:11)
在使用方法popup.show()之前,创建一个MenuPopupHelper实例并调用方法setForceShowIcon(true),就像这样
try {
Field mFieldPopup=popupMenu.getClass().getDeclaredField("mPopup");
mFieldPopup.setAccessible(true);
MenuPopupHelper mPopup = (MenuPopupHelper) mFieldPopup.get(popupMenu);
mPopup.setForceShowIcon(true);
} catch (Exception e) {
}
答案 5 :(得分:7)
我找到的最简单方法是使用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>
答案 6 :(得分:6)
我使用MenuPopupHelper.setForceShowIcon(true)
找到了原生解决方案。
private void createMenu(int menuRes, View anchor, MenuBuilder.Callback callback) {
Context context = anchor.getContext();
NavigationMenu navigationMenu = new NavigationMenu(context);
navigationMenu.setCallback(callback);
SupportMenuInflater supportMenuInflater = new SupportMenuInflater(context);
supportMenuInflater.inflate(menuRes, navigationMenu);
MenuPopupHelper menuPopupHelper = new MenuPopupHelper(context, navigationMenu, anchor);
menuPopupHelper.setForceShowIcon(true);
menuPopupHelper.show();
}
private void initMenu(View view) {
view.findViewById(R.id.myButton).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
createMenu(R.menu.help_menu, view, new MenuBuilder.Callback() {
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
switch (item.getItemId()) {
case R.id.id1:
// Do something
break;
case R.id.id2:
// Do something
break;
case R.id.id3:
// Do something
break;
}
return true;
}
@Override
public void onMenuModeChange(MenuBuilder menu) {
}
});
}
});
}
答案 7 :(得分:3)
PopupMenu不会显示图标。您可以使用ActionBar。
答案 8 :(得分:2)
上面的一些解决方案适用于反射黑客,
只是分享一下:我最近遇到了同样的问题,但我也想创建一个更自定义的东西(在菜单中添加自定义视图),所以我创建了以下的lib。
答案 9 :(得分:1)
沿着使用反射而不需要使用MenuPopupHelper
的路线,您可以添加
if (popup.getMenu() instanceof MenuBuilder) {
//noinspection RestrictedApi
((MenuBuilder) popup.getMenu()).setOptionalIconsVisible(true);
}
在充气菜单之前
答案 10 :(得分:1)
如果您使用的是将MenuPopupHelper
的可见性更改为package-private的AndroidX,则可以通过使用相同的包名称创建包装类来避免反射的代价。
这会将包私有成员公开。
package androidx.appcompat.widget // Create this package in your project's /src/main/java
import android.annotation.SuppressLint
class PopupMenuWrapper(val t: PopupMenu) {
@SuppressLint("RestrictedApi")
fun setForceShowIcon(show: Boolean) { // Public method
t.mPopup.setForceShowIcon(show)
}
}
fun PopupMenu.wrap() = PopupMenuWrapper(this)
然后像平常一样调用隐藏函数。
val popup = PopupMenu(anchor.context, anchor)
popup.wrap().setForceShowIcon(true)
popup.show()
答案 11 :(得分:1)
如果要阻止使用RestrictedApi
,请使用以下扩展功能:
fun PopupMenu.forcePopUpMenuToShowIcons() {
try {
val method = menu.javaClass.getDeclaredMethod(
"setOptionalIconsVisible",
Boolean::class.javaPrimitiveType
)
method.isAccessible = true
method.invoke(menu, true)
} catch (e: Exception) {
e.printStackTrace()
}
}
答案 12 :(得分:0)
不能完全自定义PopupMenu。在下面,您找到了一种通用的解决方案,可以通过自定义布局使PopupMenu可自定义。有了它,您可以对不同的布局进行更多的实验。干杯。
1-自定义PopupMenu类:
public class PopupMenuCustomLayout {
private PopupMenuCustomOnClickListener onClickListener;
private Context context;
private PopupWindow popupWindow;
private int rLayoutId;
private View popupView;
public PopupMenuCustomLayout(Context context, int rLayoutId, PopupMenuCustomOnClickListener onClickListener) {
this.context = context;
this.onClickListener = onClickListener;
this.rLayoutId = rLayoutId;
LayoutInflater inflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
popupView = inflater.inflate(rLayoutId, null);
int width = LinearLayout.LayoutParams.WRAP_CONTENT;
int height = LinearLayout.LayoutParams.WRAP_CONTENT;
boolean focusable = true;
popupWindow = new PopupWindow(popupView, width, height, focusable);
popupWindow.setElevation(10);
LinearLayout linearLayout = (LinearLayout) popupView;
for (int i = 0; i < linearLayout.getChildCount(); i++) {
View v = linearLayout.getChildAt(i);
v.setOnClickListener( v1 -> { onClickListener.onClick( v1.getId()); popupWindow.dismiss(); });
}
}
public void setAnimationStyle( int animationStyle) {
popupWindow.setAnimationStyle(animationStyle);
}
public void show() {
popupWindow.showAtLocation( popupView, Gravity.CENTER, 0, 0);
}
public void show( View anchorView, int gravity, int offsetX, int offsetY) {
popupWindow.showAsDropDown( anchorView, 0, -2 * (anchorView.getHeight()));
}
public interface PopupMenuCustomOnClickListener {
public void onClick(int menuItemId);
}
}
2-您的自定义布局,例如具有水平布局的linearlayout。在这种情况下,我将简单的LinearLayout与TextView项一起使用。您可以使用按钮等。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="horizontal">
<TextView
android:id="@+id/popup_menu_custom_item_a"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="A"
android:textAppearance="?android:textAppearanceMedium" />
<TextView
android:id="@+id/popup_menu_custom_item_b"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:text="B"
android:textAppearance="?android:textAppearanceMedium" />
// ...
</LinearLayout>
3-使用自定义PopupMenu像普通的PopupMenu一样。
PopupMenuCustomLayout popupMenu = new PopupMenuCustomLayout(
MainActivity.mainActivity, R.layout.popup_menu_custom_layout,
new PopupMenuCustomLayout.PopupMenuCustomOnClickListener() {
@Override
public void onClick(int itemId) {
// log statement: "Clicked on: " + itemId
switch (itemId) {
case R.id.popup_menu_custom_item_a:
// log statement: "Item A was clicked!"
break;
}
}
});
// Method 1: popupMenu.show();
// Method 2: via an anchor view:
popupMenu.show( anchorView, Gravity.CENTER, 0, 0);
答案 13 :(得分:0)
您可以使用 setForceShowIcon (true)
PopupMenu(context, view).apply {
setForceShowIcon(true)
menuInflater.inflate(R.menu.menu_edit_professional_experience, menu)
setOnMenuItemClickListener { item ->
Toast.makeText(view.context, "YOU clcick", Toast.LENGTH_LONG).show()
true
}
}.show()
使用 setForceShowIcon(true)