Android 是否可以在弹出菜单中显示图标?
我真的很喜欢3.0中的新弹出菜单,但我就是不能在菜单项旁边显示任何图标。我正在从下面的.xml扩展菜单:Android 是否可以在弹出菜单中显示图标?,android,android-3.0-honeycomb,Android,Android 3.0 Honeycomb,我真的很喜欢3.0中的新弹出菜单,但我就是不能在菜单项旁边显示任何图标。我正在从下面的.xml扩展菜单: 我无法显示图标,我缺少什么吗?弹出菜单将不显示图标。您可以使用ActionBar 如果你愿意冒险一点,看看谷歌的弹出菜单源代码。创建您自己的类,即MyPopupMenu,它与Google的PopupMenu类相同,但做一点小小的更改 在弹出菜单的构造函数中: public MyPopupMenu(Context context, View anchor) { // TODO Th
我无法显示图标,我缺少什么吗?弹出菜单将不显示图标。您可以使用ActionBar
如果你愿意冒险一点,看看谷歌的弹出菜单源代码。创建您自己的类,即MyPopupMenu,它与Google的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强制它显示图标。您也可以根据需要公开一个公共方法来设置此标志。我可以使用反射来显示图标。这可能不是最优雅的解决方案,但它确实有效
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();
}
试试看{
Class classPopupMenu=Class.forName(popupMenu
.getClass().getName());
字段mPopup=classPopupMenu.getDeclaredField(“mPopup”);
mPopup.setAccessible(true);
对象菜单opupPhelper=mPopup.get(弹出菜单);
Class CLASSPOPUSCURLPER=Class.forName(menuPopupHelper
.getClass().getName());
方法setForceIcons=classPopucUpCelper.getMethod(
“setForceShowIcon”,boolean.class);
调用(menuPopupHelper,true);
}捕获(例外e){
e、 printStackTrace();
}
对盖兰·博尔格提供的解决方案的贡献。
如果您得到“IllegalAccessException:不允许访问字段”,请使用此代码
PopupMenu popup=新的PopupMenu(mContext,视图);
试一试{
Field[]fields=popup.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();
}
prepareMenu(popup.getMenu());
popup.show();
text我们可以使用子菜单模型。所以,我们不需要编写显示弹出菜单的方法,它将自动显示。看看: 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>
<?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>
结果是:
上面的一些解决方案将与反射黑客一起工作 分享一下:我最近遇到了同样的问题,但我也想创建一个更定制的东西(在菜单中添加自定义视图),所以我创建了以下库
在使用方法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) {
}
我使用
菜单opuphelper.setForceShowIcon(true)
找到了一个本机解决方案
用法
我发现最简单的方法是使用
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
<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>
<?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>
在使用反射的过程中,您可以添加
if (popup.getMenu() instanceof MenuBuilder) {
//noinspection RestrictedApi
((MenuBuilder) popup.getMenu()).setOptionalIconsVisible(true);
}
在弹出菜单之前,无法完全自定义弹出菜单。在下面,您可以找到一个通用的解决方案,使您的弹出菜单可以通过自定义布局进行自定义。有了这些,你可以用不同的布局做更多的实验。干杯 1-自定义弹出菜单类:
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);
}
}
如果您使用的是AndroidX,它将
MenuPopupHelper
的可见性更改为package private,那么您可以通过创建具有相同包名的包装器类来避免反射成本
这将向公众公开包私有成员
package androidx.appcompat.widget//在项目的/src/main/java中创建此包
导入android.annotation.SuppressLint
类PopupMenuRapper(val t:PopupMenu){
@SuppressLint(“RestrictedApi”)
fun setForceShowIcon(show:Boolean){//Public方法
t、 mPopup.setForceShowIcon(显示)
}
}
fun PopupMenu.wrap()=PopupMenuWrapper(此)
然后像平常一样调用隐藏函数
val popup=popup菜单(anchor.context,anchor)
popup.wrap().setForceShowIcon(真)
popup.show()
如果要防止使用受限API
请使用此扩展功能:
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()
}
}
您可以使用setForceShowIcon(真)
使用setForceShowIcon(true)为了创建自己的弹出菜单,您需要进入内部,所以这不是一个好主意。应该如何处理MenuBuilder或MenuPoupHelper类?如果我记得,我使用的是ActionSherlockLibrary,所以我使用了MenuBuilder和MenuPopupHelper的等价物。Android上的“深入内部”有什么不对?这就是安卓的全部要点——这不是iOS。回答得很好,谢谢@Robert!如何在androidx中获取“MenuBuilder”实例?请确保在“mPopup”字段中调用Field.setAccessible。我成功地使用它在我的
弹出菜单中显示图标。伟大的(将在android源代码更改时中断,对吗?)禁用proguard时,此功能运行良好。启用proguard后,您知道我应该在proguard project.txt中添加哪些规则吗?我想,我不熟悉proguard的配置,只是反复尝试。添加以下2条规则后,当启用proguard时,代码再次工作。(注意,我使用的是support package中的PopupMenu)-keepclassmembernames类android.support.v7.widget.PopupMenu{private android.support.v7.internal.view.menu.MenuPopupPer MPoppup;}-keepclassmembernames类android.support.v7.internal.view.menu.MenupOpupPer{public void setForceShowIcon(布尔);}很好的解决方案。
<?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>
if (popup.getMenu() instanceof MenuBuilder) {
//noinspection RestrictedApi
((MenuBuilder) popup.getMenu()).setOptionalIconsVisible(true);
}
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);
}
}
<?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>
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);
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()
}
}
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()