Я знаю, что это невозможно с использованием собственного API. Есть ли обходной путь для реализации такого представления?
Как отобразить значки в меню переполнения в панели действий
Ответы (15)
Ранее опубликованный ответ в целом в порядке. Но это в основном удаляет поведение по умолчанию меню переполнения. Такие вещи, как количество значков, которые могут отображаться на разных размерах экрана, а затем они выпадают в дополнительное меню, когда не могут отображаться. Делая вышеперечисленное, вы удаляете много важных функций.
Лучшим методом было бы указать дополнительному меню отображать значки напрямую. Вы можете сделать это, добавив следующий код в свой Activity.
@Override
public boolean onMenuOpened(int featureId, Menu menu)
{
if(featureId == Window.FEATURE_ACTION_BAR && menu != null){
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
try{
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
}
catch(NoSuchMethodException e){
Log.e(TAG, "onMenuOpened", e);
}
catch(Exception e){
throw new RuntimeException(e);
}
}
}
return super.onMenuOpened(featureId, menu);
}
com.android.internal.view.MenuBuilder
), мне интересно, насколько он надежен, особенно когда выпускаются новые уровни API. Надеемся, что Google когда-нибудь представит эту функциональность в общедоступном API (хотя этот ответ Романа Нурика из Google не обнадеживает).
- person Ted Hopp; 15.07.2014
android:title
должно быть большим нет-нет.
- person TWiStErRob; 30.11.2014
onMenuOpened(FEATURE_ACTION_BAR)
больше не вызывается в appcompat-v7:22.x
, это может быть или не быть преднамеренным, см. b.android.com/171440 . Можно переместить код в onPrepareOptionsMenu
.
- person TWiStErRob; 03.05.2015
featureId == Window.FEATURE_ACTION_BAR
не работает, и я исправляю это с помощью (featureId & Window.FEATURE_ACTION_BAR) == Window.FEATURE_ACTION_BAR
, работает!
- person Ninja; 12.01.2016
onCreateOptionsMenu(...)
работает
- person Louis CAD; 16.08.2016
-keepclassmembers class **.MenuBuilder { void setOptionalIconsVisible(boolean); }
- person trooper; 30.08.2016
Accessing internal APIs via reflection is not supported and may not work on all devices or in the future less... Using reflection to access hidden/private Android APIs is not safe; it will often not work on devices from other vendors, and it may suddenly stop working (if the API is removed) or crash spectacularly (if the API behavior changes, since there are no guarantees for compatibility).
- person miguelt; 20.07.2018
Using reflection to access hidden/private Android APIs is not safe; it will often not work on devices from other vendors, and it may suddenly stop working (if the API is removed) or crash spectacularly (if the API behavior changes, since there are no guarantees for compatibility).
; При использовании кастинга: MenuBuilder.setOptionalIconsVisible can only be called from within the same library group (groupId=androidx.appcompat) less...
- MenuBuilder
помечены как @RestrictTo(LIBRARY_GROUP)
- person miguelt; 20.07.2018
В вашем меню xml используйте следующий синтаксис для вложения меню, вы начнете получать меню со значками
<item
android:id="@+id/empty"
android:icon="@drawable/ic_action_overflow"
android:orderInCategory="101"
android:showAsAction="always">
<menu>
<item
android:id="@+id/action_show_ir_list"
android:icon="@drawable/ic_menu_friendslist"
android:showAsAction="always|withText"
android:title="List"/>
</menu>
</item>
MenuBuilder
как @RestrictTo(LIBRARY_GROUP)
)
- person miguelt; 20.07.2018
Попробовал это на основе предыдущих ответов, и он отлично работает, по крайней мере, с более поздними версиями библиотеки поддержки (25.1):
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
if(menu instanceof MenuBuilder){
MenuBuilder m = (MenuBuilder) menu;
//noinspection RestrictedApi
m.setOptionalIconsVisible(true);
}
return true;
}
MenuBuilder.setOptionalIconsVisible
можно вызывать только из той же группы библиотек (groupId=com.android.support)
- person Francesco Vadicamo; 13.08.2017
@SuppressLint("RestrictedApi")
- person tim4dev; 19.07.2018
Вы можете использовать SpannableString
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_tab, menu);
MenuItem item = menu.findItem(R.id.action_login);
SpannableStringBuilder builder = new SpannableStringBuilder("* Login");
// replace "*" with icon
builder.setSpan(new ImageSpan(this, R.drawable.login_icon), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
item.setTitle(builder);
}
Ответ от Саймона был очень полезен для меня, поэтому я хочу поделиться тем, как я реализовал его в методе onCreateOptionsMenu
, как было предложено:
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu items for use in the action bar
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_action_bar, menu);
// To show icons in the actionbar's overflow menu:
// http://stackoverflow.com/questions/18374183/how-to-show-icons-in-overflow-menu-in-actionbar
//if(featureId == Window.FEATURE_ACTION_BAR && menu != null){
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
try{
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
}
catch(NoSuchMethodException e){
Log.e(TAG, "onMenuOpened", e);
}
catch(Exception e){
throw new RuntimeException(e);
}
}
//}
return super.onCreateOptionsMenu(menu);
}
package android.support.v7.view.menu; import android.view.Menu; public class Menus { public static void setOptionalIconsVisible(Menu menu) { if (menu instanceof MenuBuilder) { MenuBuilder menuBuilder = (MenuBuilder) menu; menuBuilder.setOptionalIconsVisible(true); } } }
- person Makotosan; 03.03.2016
Основываясь на ответе @Desmond Lua из выше, я создал статический метод для использования drawable, объявленного в XML в раскрывающемся списке, и гарантируя, что его окрашенный цвет не влияет на состояние Constant Drawable.
/**
* Updates a menu item in the dropdown to show it's icon that was declared in XML.
*
* @param item
* the item to update
* @param color
* the color to tint with
*/
private static void updateMenuWithIcon(@NonNull final MenuItem item, final int color) {
SpannableStringBuilder builder = new SpannableStringBuilder()
.append("*") // the * will be replaced with the icon via ImageSpan
.append(" ") // This extra space acts as padding. Adjust as you wish
.append(item.getTitle());
// Retrieve the icon that was declared in XML and assigned during inflation
if (item.getIcon() != null && item.getIcon().getConstantState() != null) {
Drawable drawable = item.getIcon().getConstantState().newDrawable();
// Mutate this drawable so the tint only applies here
drawable.mutate().setTint(color);
// Needs bounds, or else it won't show up (doesn't know how big to be)
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan imageSpan = new ImageSpan(drawable);
builder.setSpan(imageSpan, 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
item.setTitle(builder);
}
}
И его использование будет выглядеть примерно так при использовании в действии. Это, вероятно, может быть даже более элегантным в зависимости от ваших индивидуальных потребностей.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_activity_provider_connect, menu);
int color = ContextCompat.getColor(this, R.color.accent_dark_grey);
updateMenuWithIcon(menu.findItem(R.id.email), color);
updateMenuWithIcon(menu.findItem(R.id.sms), color);
updateMenuWithIcon(menu.findItem(R.id.call), color);
return true;
}
Текущее лучшее, но не принятое решение, вероятно, работает на старых платформах. В любом случае в новом AppCompat21+ требуемый метод не существует, а метод getDeclaredMethod
возвращает исключение NoSuchMethodException
.
Поэтому обходной путь для меня (протестирован и работает на устройствах 4.x, 5.x) основан на прямом изменении фонового параметра. Так что просто поместите этот код в свой класс Activity.
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
// enable visible icons in action bar
if (featureId == Window.FEATURE_ACTION_BAR && menu != null) {
if (menu.getClass().getSimpleName().equals("MenuBuilder")) {
try {
Field field = menu.getClass().
getDeclaredField("mOptionalIconsVisible");
field.setAccessible(true);
field.setBoolean(menu, true);
} catch (IllegalAccessException | NoSuchFieldException e) {
Logger.w(TAG, "onMenuOpened(" + featureId + ", " + menu + ")", e);
}
}
}
return super.onMenuOpened(featureId, menu);
}
-keepclassmembers **.MenuBuilder { void setOptionalIconsVisible(boolean); }
- person TWiStErRob; 03.05.2015
Самый простой способ, который я нашел, это:
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.toolbar_menu,menu);
if(menu instanceof MenuBuilder) { //To display icon on overflow menu
MenuBuilder m = (MenuBuilder) menu;
m.setOptionalIconsVisible(true);
}
return true;
} `
Ответ @Simon действительно работает хорошо... но если вы используете AppCompat Activity... вместо этого вам нужно будет использовать этот код... Потому что onMenuOpened() больше не вызывается в appcompat-v7:22.x
@Override
protected boolean onPrepareOptionsPanel(View view, Menu menu) {
if(menu != null){
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
try{
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
}
catch(NoSuchMethodException e){
Log.e(Constants.DEBUG_LOG, "onMenuOpened", e);
}
catch(Exception e){
throw new RuntimeException(e);
}
}
}
return super.onPrepareOptionsPanel(view, menu);
}
Мой простой мод для отличного решения Саймона для использования с ActionMode:
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
if(menu != null){
if(menu.getClass().getSimpleName().equals("MenuBuilder")){
try{
Method m = menu.getClass().getDeclaredMethod(
"setOptionalIconsVisible", Boolean.TYPE);
m.setAccessible(true);
m.invoke(menu, true);
}
catch(NoSuchMethodException e){
Log.e(TAG, "onPrepareActionMode", e);
}
catch(Exception e){
throw new RuntimeException(e);
}
}
}
return true;
}
По моему мнению, это возможно только путем создания пользовательской панели инструментов. Потому что ActionBar по умолчанию не дает вам этой функции. Но вы можете поместить значки, взяв подменю в качестве дочернего элемента элемента. И если у вас есть лучшее решение, чем у меня... просто сообщите мне.
<?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/action_settings"
android:icon="@drawable/ic_menu_camera"
android:showAsAction="never"
android:title="@string/action_settings" />
<item
android:id="@+id/action_1"
android:icon="@drawable/ic_menu_gallery"
android:showAsAction="never"
android:title="Hello" />
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_search_category_default"
android:showAsAction="never"
android:title="action_search">
<menu>
<item
android:id="@+id/version1"
android:icon="@android:drawable/ic_dialog_alert"
android:showAsAction="never"
android:title="Cup cake" />
<item
android:id="@+id/version2"
android:icon="@drawable/ic_menu_camera"
android:showAsAction="never"
android:title="Donut" />
<item
android:id="@+id/version3"
android:icon="@drawable/ic_menu_send"
android:showAsAction="never"
android:title="Eclair" />
<item
android:id="@+id/version4"
android:icon="@drawable/ic_menu_gallery"
android:showAsAction="never"
android:title="Froyo" />
</menu>
</item>
</menu>
котлин:
@SuppressLint("RestrictedApi")
fun Menu.showOptionalIcons() {
this as MenuBuilder
setOptionalIconsVisible(true)
}
Это слишком поздно, но кто-то может помочь мне попытаться, я получил помощь от ответа @Desmond Lua, который помогает тем, кто использует menu.xml
Мой ответ для создания динамического меню, вот мой код:
int ACTION_MENU_ID =1;
SpannableStringBuilder builder;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
//this space for icon
builder = new SpannableStringBuilder(" " + getString(R.string.your_menu_title));
builder.setSpan(new ImageSpan(this, R.drawable.ic_your_menu_icon), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
//dynamic menu added
menu.add(Menu.NONE,ACTION_MENU_ID, Menu.NONE,
getString(R.string.your_menu_title))
.setShowAsAction(MenuItemCompat.SHOW_AS_ACTION_WITH_TEXT | MenuItemCompat.SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW);
//set icon in overflow menu
menu.findItem(ACTION_MENU_ID).setTitle(builder);
}
Добавьте это в стиле:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_settings"
app:showAsAction="always"
android:icon="@drawable/ic_more_vert_white"
android:orderInCategory="100"
android:title="">
<menu>
<item
android:id="@+id/Login"
android:icon="@drawable/ic_menu_user_icon"
android:showAsAction="collapseActionView|withText"
android:title="@string/str_Login" />
<item
android:id="@+id/str_WishList"
android:icon="@drawable/ic_menu_wish_list_icon"
android:showAsAction="collapseActionView"
android:title="@string/str_WishList" />
<item
android:id="@+id/TrackOrder"
android:icon="@drawable/ic_menu_my_order_icon"
android:showAsAction="collapseActionView"
android:title="@string/str_TrackOrder" />
<item
android:id="@+id/Ratetheapp"
android:icon="@drawable/ic_menu_rate_the_apps"
android:showAsAction="collapseActionView"
android:title="@string/str_Ratetheapp" />
<item
android:id="@+id/Sharetheapp"
android:icon="@drawable/ic_menu_shar_the_apps"
android:showAsAction="collapseActionView"
android:title="@string/str_Sharetheapp" />
<item
android:id="@+id/Contactus"
android:icon="@drawable/ic_menu_contact"
android:showAsAction="collapseActionView"
android:title="@string/str_Contactus" />
<item
android:id="@+id/Policies"
android:icon="@drawable/ic_menu_policy_icon"
android:showAsAction="collapseActionView"
android:title="@string/str_Policies" />
</menu>
</item>
</menu>
Я воспользовался предложением MashukKhan, но я всегда показывал элемент держателя как действие и использовал больше точек по вертикали из векторных активов в качестве значка.
<item
android:orderInCategory="10"
android:title=""
android:icon="@drawable/ic_more_vert"
app:showAsAction="always" >
<menu>
<item
android:id="@+id/action_tst1"
...and so on
</menu>
</item>
Это создает эффект «переполнения» меню и отображает значки в раскрывающемся списке.
Можно даже поместить элементы перед ним, если они не конфликтуют с его отображением.
Я нахожу решение MashukKhan элегантным и оно подходит для решения, а не для обхода категории, потому что это просто реализация подменю.