android - mainactivity - PopupMenu con iconos
popup menu animation android (12)
Esta pregunta ya tiene una respuesta aquí:
- ¿Es posible mostrar íconos en un PopupMenu? 11 respuestas
Por supuesto, estamos tratando aquí con SDK 11 y superior.
Tengo la intención de hacer algo similar a esto:
Al lado de cada elemento en ese PopupMenu
, me gustaría colocar un ícono .
Creé un archivo XML
y lo coloqué en /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>
Como habrás notado, en el archivo xml estoy definiendo los íconos que quiero, sin embargo, cuando se muestra el menú emergente, se muestran sin los íconos. ¿Qué debo hacer para que aparezcan esos 2 íconos?
Basado en la respuesta de @Ajay ... esto es lo que hice
@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);
}
}
surgir
<?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>
Captura de pantalla
De esta manera funciona si está utilizando AppCompat v7. Es un poco hacky pero significativamente mejor que el uso de la reflexión y te permite seguir utilizando el núcleo de 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>
Esto da como resultado el menú emergente utilizando el icono que está definido en su archivo de recursos de menú:
El menú emergente de Android tiene un método oculto para mostrar el ícono del menú. Use la reflexión de Java para habilitarlo como el siguiente fragmento de código.
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();
}
}
Estaba intentando la respuesta de @Stephen Kidson y la sugerencia de @david.schereiber, y me di cuenta de que no existe tal método setOnMenuItemClickListener
en MenuBuilder
. Me revolví un poco con el código fuente del v7 y encontré esta solución:
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();
La clase MenuPopupHelper
en AppCompat tiene la anotación @hide
. Si eso es una preocupación, o si no puede usar AppCompat por cualquier razón, hay otra solución que utiliza un Spannable
en el título de MenuItem
que contiene tanto el icono como el texto del título.
Los pasos principales son:
- infle su
PopupMenu
con un archivo demenu
xml - si alguno de los elementos tiene un ícono, hazlo para todos los ítems:
- si el elemento no tiene un ícono, crea un ícono transparente. Esto asegura que los elementos sin iconos se alinearán con elementos con iconos
- crear un
SpannableStringBuilder
contenga el icono y el título - establecer el título del elemento de menú en
SpannableStringBuilder
- establecer el ícono del elemento del menú como nulo, "por las dudas"
Pros: Sin reflejo. No usa ninguna apis escondida. Puede trabajar con el marco PopupMenu.
Contras: Más código. Si tiene un submenú sin un ícono, tendrá relleno izquierdo indeseado en una pantalla pequeña.
Detalles:
Primero, defina un tamaño para el icono en un archivo dimens.xml
:
<dimen name="menu_item_icon_size">24dp</dimen>
Luego, algunos métodos para mover los íconos definidos en xml a los títulos:
/**
* 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);
}
Finalmente, crea tu PopupMenu y utiliza los métodos anteriores antes de mostrarlo:
PopupMenu popupMenu = new PopupMenu(view.getContext(), view);
popupMenu.inflate(R.menu.popup_menu);
insertMenuItemIcons(textView.getContext(), popupMenu);
popupMenu.show();
Lee el código fuente de PopupMenu. Podemos mostrar el icono por el siguiente código:
Field field = popupMenu.getClass().getDeclaredField("mPopup");
field.setAccessible(true);
MenuPopupHelper menuPopupHelper = (MenuPopupHelper) field.get(popupMenu);
menuPopupHelper.setForceShowIcon(true);
Pero MenuPopupHelper.java está en el paquete interno de Android. Entonces deberíamos usar 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();
Lo implementaría de otra manera:
Crear un diseño 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>
y luego crea PopUpWindow
en tu 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);
}
finalmente, onClick
clic en el botón o cualquier otra cosa:
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);
}
});
Buen ejemplo para PopUpWindow
:
http://androidresearch.wordpress.com/2012/05/06/how-to-create-popups-in-android/
Menú emergente con icono usando MenuBuilder
y 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>
Puede implementar esto. Mediante el uso de Reflection, si no está familiarizado con la ayuda de esta asombrosa función avanzada de Java, puede modificar el comportamiento en el tiempo de ejecución de las aplicaciones que se ejecutan en la JVM. Puede ver el objeto y realizar sus métodos en tiempo de ejecución. y en nuestro caso, necesitamos modificar el comportamiento de popupMenu en tiempo de ejecución en lugar de extender la clase principal y modificarla;) espero que la ayuda
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();
}
Si desea mostrar el ícono en el menú emergente, eche un vistazo a https://github.com/shehabic/Droppy , es bastante bueno y fácil de usar
Solucioné mi problema de la manera más simple posible, nunca esperé tal simplicidad:
En 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>
en 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;
}
Eso fue un truco al usar un submenú
list_item_menu.xml en el directorio / res / menu
<?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>
En mi actividad
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();
}
resultado