Как изменить цвет фона в меню параметров?

Я пытаюсь изменить цвет по умолчанию для меню опций, которое является белым: я хочу черный фон для каждого элемента меню опций.

Я пробовал некоторые побеги, такие как android: itemBackground = "# 000000" элемента элемента в элементе меню, но он не работал.

Как я могу это сделать?

Это явно проблема, которую испытывает множество программистов, и которой Google еще не предоставил удовлетворительное, поддерживаемое решение.

Есть много скрещенных намерений и недоразумений, плавающих вокруг сообщений по этой теме, поэтому, пожалуйста, прочитайте весь этот ответ, прежде чем отвечать.

Ниже я включаю более «утонченную» и хорошо прокомментированную версию взлома из других ответов на этой странице, также включающую идеи из этих очень близких вопросов:

Изменение цвета фона в меню Android

Как изменить цвет фона в меню параметров?

Android: настроить меню приложения (например, цвет фона)

http://www.macadamian.com/blog/post/android_-_theming_the_unthemable/

Кнопка Android MenuItem Toggle

Возможно ли сделать фон меню опций Android непрозрачным?

http://www.codeproject.com/KB/android/AndroidMenusMyWay.aspx

Установка фона меню непрозрачным

Я тестировал этот хак на 2.1 (симулятор), 2.2 (2 реальных устройства) и 2.3 (2 реальных устройства). У меня нет никаких 3.X-планшетов для тестирования, но я буду публиковать любые необходимые изменения здесь, когда / если я это сделаю. Учитывая, что 3.X-планшеты используют Action Bars вместо меню Options, как описано здесь:

http://developer.android.com/guide/topics/ui/menus.html#options-menu

Этот хак почти наверняка ничего не сделает (никакого вреда и ничего хорошего) на таблетках 3.X.

ЗАЯВЛЕНИЕ О ПРОБЛЕМЕ (прочитайте это до ответа триггера с отрицательным комментарием):

Меню «Опции» имеет разные стили на разных устройствах. Чистый черный с белым текстом на некоторых, чистый белый с черным текстом на некоторых. Я и многие другие разработчики хотят контролировать цвет фона в ячейках меню «Параметры», а также цвет текста меню «Параметры» .

Некоторым разработчикам приложений нужно установить цвет фона ячейки (не цвет текста), и они могут сделать это более чистым способом, используя стиль android: panelFullBackground, описанный в другом ответе. Однако в настоящее время нет способа управлять цветом текста меню «Параметры» со стилями, и поэтому этот метод можно использовать только для изменения фона на другой цвет, который не заставит текст «исчезнуть».

Мы с удовольствием сделаем это с документально подтвержденным, надежным решением, но его просто нет на Android <= 2.3. Поэтому мы должны использовать решение, которое работает в текущих версиях и предназначено для минимизации вероятности сбоя / взлома в будущих версиях. Нам нужно решение, которое изящно не возвращается к поведению по умолчанию, если оно должно потерпеть неудачу.

Существует много законных причин, по которым может потребоваться контролировать внешний вид меню «Опции» (как правило, чтобы соответствовать визуальному стилю для остальной части приложения), поэтому я не буду останавливаться на этом.

Об этом сообщается об ошибке Google Android: добавьте свою поддержку, сняв эту ошибку (обратите внимание, что Google отговаривает «меня тоже»): просто звезды достаточно):

http://code.google.com/p/android/issues/detail?id=4441

РЕЗЮМЕ РЕШЕНИЙ:

Несколько плакатов предложили взлом с участием LayoutInflater.Factory. Предлагаемый взлом работал для Android <= 2.2 и не удался для Android 2.3, потому что хак сделал недокументированное предположение: можно было напрямую вызвать LayoutInflater.getView (), не находясь внутри вызова LayoutInflater.inflate () в том же экземпляре LayoutInflater. Новый код в Android 2.3 нарушил это предположение и привел к исключению NullPointerException.

Мой слегка утонченный хак ниже не полагается на это предположение.

Кроме того, хаки также полагаются на использование внутреннего недокументированного имени класса «com.android.internal.view.menu.IconMenuItemView» в виде строки (а не как типа Java). Я не вижу никакого способа избежать этого и выполнить поставленную цель. Тем не менее, можно сделать взломать осторожным способом, который отпадет, если «com.android.internal.view.menu.IconMenuItemView» не появится в текущей системе.

Опять же, поймите, что это взломать и ни в коем случае не утверждаю, что это будет работать на всех платформах. Но мы, разработчики, не живем в фантастическом академическом мире, где все должно быть по книге: у нас есть проблема для решения, и мы должны решить ее как можно лучше. Например, маловероятно, что «com.android.internal.view.menu.IconMenuItemView» будет существовать на таблетках 3.X, поскольку они используют Action Bars вместо меню Options.

Наконец, некоторые разработчики решили эту проблему, полностью подавив меню «Настройки Android» и написав свой собственный класс меню (см. Некоторые из приведенных выше ссылок). Я не пробовал это, но если у вас есть время написать свой собственный просмотр и выяснить, как заменить вид Android (я уверен, что черт в деталях здесь), то это может быть приятное решение, которое не требует каких-либо Недокументированные хаки.

HACK:

Вот код.

Чтобы использовать этот код, вызовите addOptionsMenuHackerInflaterFactory () ONCE из вашей активности onCreate () или вашей активности onCreateOptionsMenu (). Он устанавливает фабрику по умолчанию, которая повлияет на последующее создание любого меню параметров. Он не влияет на уже созданные меню параметров (предыдущие хаки использовали имя функции setMenuBackground (), что очень вводит в заблуждение, так как функция не устанавливает никаких свойств меню до того, как она вернется).

@SuppressWarnings("rawtypes") static Class IconMenuItemView_class = null; @SuppressWarnings("rawtypes") static Constructor IconMenuItemView_constructor = null; // standard signature of constructor expected by inflater of all View classes @SuppressWarnings("rawtypes") private static final Class[] standard_inflater_constructor_signature = new Class[] { Context.class, AttributeSet.class }; protected void addOptionsMenuHackerInflaterFactory() { final LayoutInflater infl = getLayoutInflater(); infl.setFactory(new Factory() { public View onCreateView(final String name, final Context context, final AttributeSet attrs) { if (!name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) return null; // use normal inflater View view = null; // "com.android.internal.view.menu.IconMenuItemView" // - is the name of an internal Java class // - that exists in Android <= 3.2 and possibly beyond // - that may or may not exist in other Android revs // - is the class whose instance we want to modify to set background etc. // - is the class we want to instantiate with the standard constructor: // IconMenuItemView(context, attrs) // - this is what the LayoutInflater does if we return null // - unfortunately we cannot just call: // infl.createView(name, null, attrs); // here because on Android 3.2 (and possibly later): // 1. createView() can only be called inside inflate(), // because inflate() sets the context parameter ultimately // passed to the IconMenuItemView constructor's first arg, // storing it in a LayoutInflater instance variable. // 2. we are inside inflate(), // 3. BUT from a different instance of LayoutInflater (not infl) // 4. there is no way to get access to the actual instance being used // - so we must do what createView() would have done for us // if (IconMenuItemView_class == null) { try { IconMenuItemView_class = getClassLoader().loadClass(name); } catch (ClassNotFoundException e) { // this OS does not have IconMenuItemView - fail gracefully return null; // hack failed: use normal inflater } } if (IconMenuItemView_class == null) return null; // hack failed: use normal inflater if (IconMenuItemView_constructor == null) { try { IconMenuItemView_constructor = IconMenuItemView_class.getConstructor(standard_inflater_constructor_signature); } catch (SecurityException e) { return null; // hack failed: use normal inflater } catch (NoSuchMethodException e) { return null; // hack failed: use normal inflater } } if (IconMenuItemView_constructor == null) return null; // hack failed: use normal inflater try { Object[] args = new Object[] { context, attrs }; view = (View)(IconMenuItemView_constructor.newInstance(args)); } catch (IllegalArgumentException e) { return null; // hack failed: use normal inflater } catch (InstantiationException e) { return null; // hack failed: use normal inflater } catch (IllegalAccessException e) { return null; // hack failed: use normal inflater } catch (InvocationTargetException e) { return null; // hack failed: use normal inflater } if (null == view) // in theory handled above, but be safe... return null; // hack failed: use normal inflater // apply our own View settings after we get back to runloop // - android will overwrite almost any setting we make now final View v = view; new Handler().post(new Runnable() { public void run() { v.setBackgroundColor(Color.BLACK); try { // in Android <= 3.2, IconMenuItemView implemented with TextView // guard against possible future change in implementation TextView tv = (TextView)v; tv.setTextColor(Color.WHITE); } catch (ClassCastException e) { // hack failed: do not set TextView attributes } } }); return view; } }); } 

Спасибо за чтение и наслаждение!

Проведя значительное количество времени, пытаясь использовать все параметры, единственный способ получить приложение с помощью AppCompat v7 для изменения фона меню переполнения – использовать атрибут itemBackground:

 <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> ... <item name="android:itemBackground">@color/overflow_background</item> ... </style> 

Протестировано с API 4.2 до 5.0.

Атрибут стиля для фона меню – android:panelFullBackground .

Несмотря на то, что говорит документация, он должен быть ресурсом (например, @android:color/black или @drawable/my_drawable ), он будет сбой, если вы используете значение цвета напрямую.

Это также избавит вас от границ элементов, которые я не смог изменить или удалить с помощью решения primalpop.

Что касается цвета текста, я не нашел способа установить его в стилях в 2.2, и я уверен, что все пробовал (так я обнаружил атрибут фона меню). Для этого вам нужно будет использовать решение primalpop.

Для Android 2.3 это может быть сделано с очень тяжелым взломом:

Основной причиной проблем с Android 2.3 является то, что в LayoutInflater mConstructorArgs [0] = mContext устанавливается только во время выполнения вызовов

http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/android/view/LayoutInflater.java/#352

  protected void setMenuBackground(){ getLayoutInflater().setFactory( new Factory() { @Override public View onCreateView (final String name, final Context context, final AttributeSet attrs ) { if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) { try { // Ask our inflater to create the view final LayoutInflater f = getLayoutInflater(); final View[] view = new View[1]: try { view[0] = f.createView( name, null, attrs ); } catch (InflateException e) { hackAndroid23(name, attrs, f, view); } // Kind of apply our own background new Handler().post( new Runnable() { public void run () { view.setBackgroundResource( R.drawable.gray_gradient_background); } } ); return view; } catch ( InflateException e ) { } catch ( ClassNotFoundException e ) { } } return null; } }); } static void hackAndroid23(final String name, final android.util.AttributeSet attrs, final LayoutInflater f, final TextView[] view) { // mConstructorArgs[0] is only non-null during a running call to inflate() // so we make a call to inflate() and inside that call our dully XmlPullParser get's called // and inside that it will work to call "f.createView( name, null, attrs );"! try { f.inflate(new XmlPullParser() { @Override public int next() throws XmlPullParserException, IOException { try { view[0] = (TextView) f.createView( name, null, attrs ); } catch (InflateException e) { } catch (ClassNotFoundException e) { } throw new XmlPullParserException("exit"); } }, null, false); } catch (InflateException e1) { // "exit" ignored } } 

(Не стесняйтесь проголосовать за этот ответ;)) Я тестировал его для работы на Android 2.3 и продолжал работу над более ранними версиями. Если что-то сломается снова в более поздних версиях Android, вы просто увидите стиль меню по умолчанию вместо

Просто столкнулся с этой проблемой, в приложении, которое должно было быть совместимо с Gingerbread и по-прежнему сохранять как можно больше стилей с устройств с поддержкой Holo.

Я нашел относительно чистое решение, которое работало нормально для меня.

В теме я использую 9-патч для рисования фона, чтобы получить пользовательский цвет фона:

 <style name="Theme.Styled" parent="Theme.Sherlock"> ... <item name="android:panelFullBackground">@drawable/menu_hardkey_panel</item> </style> 

Я отказался от попыток стилизовать цвет текста и просто использовал Spannable для установки цвета текста для моего элемента в коде:

 @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getSupportMenuInflater(); inflater.inflate(R.menu.actions_main, menu); if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB) { SpannableStringBuilder text = new SpannableStringBuilder(); text.append(getString(R.string.action_text)); text.setSpan(new ForegroundColorSpan(Color.WHITE), 0, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); MenuItem item1 = menu.findItem(R.id.action_item1); item1.setTitle(text); } return true; } 

Одно дело отметить, что вы, ребята, слишком усложняете проблему, как и многие другие сообщения! Все, что вам нужно сделать, это создать гибкие селектора с любыми фонами, которые вам нужны, и установить их для фактических элементов. Я просто потрачу два часа на то, чтобы попробовать ваши решения (все они предложены на этой странице), и никто из них не работал. Не говоря уже о том, что существует множество ошибок, которые существенно замедляют вашу производительность в тех блоках try / catch, которые у вас есть.

В любом случае здесь находится файл xml в меню:

 <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/m1" android:icon="@drawable/item1_selector" /> <item android:id="@+id/m2" android:icon="@drawable/item2_selector" /> </menu> 

Теперь в вашем item1_selector:

 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/item_highlighted" /> <item android:state_selected="true" android:drawable="@drawable/item_highlighted" /> <item android:state_focused="true" android:drawable="@drawable/item_nonhighlighted" /> <item android:drawable="@drawable/item_nonhighlighted" /> </selector> 

В следующий раз, когда вы решите пойти в супермаркет через Канаду, попробуйте Google Maps!

  /* *The Options Menu (the one that pops up on pressing the menu button on the emulator) * can be customized to change the background of the menu *@primalpop */ package com.pop.menu; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Handler; import android.util.AttributeSet; import android.util.Log; import android.view.InflateException; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.View; import android.view.LayoutInflater.Factory; public class Options_Menu extends Activity { private static final String TAG = "DEBUG"; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); } /* Invoked when the menu button is pressed */ @Override public boolean onCreateOptionsMenu(Menu menu) { // TODO Auto-generated method stub super.onCreateOptionsMenu(menu); MenuInflater inflater = new MenuInflater(getApplicationContext()); inflater.inflate(R.menu.options_menu, menu); setMenuBackground(); return true; } /*IconMenuItemView is the class that creates and controls the options menu * which is derived from basic View class. So We can use a LayoutInflater * object to create a view and apply the background. */ protected void setMenuBackground(){ Log.d(TAG, "Enterting setMenuBackGround"); getLayoutInflater().setFactory( new Factory() { @Override public View onCreateView ( String name, Context context, AttributeSet attrs ) { if ( name.equalsIgnoreCase( "com.android.internal.view.menu.IconMenuItemView" ) ) { try { // Ask our inflater to create the view LayoutInflater f = getLayoutInflater(); final View view = f.createView( name, null, attrs ); /* * The background gets refreshed each time a new item is added the options menu. * So each time Android applies the default background we need to set our own * background. This is done using a thread giving the background change as runnable * object */ new Handler().post( new Runnable() { public void run () { view.setBackgroundResource( R.drawable.background); } } ); return view; } catch ( InflateException e ) {} catch ( ClassNotFoundException e ) {} } return null; } }); } } 

Спасибо, Маркус! Он работает на 2.3 плавно, исправляя некоторые синтаксические ошибки, вот фиксированный код

  protected void setMenuBackground() { getLayoutInflater().setFactory(new Factory() { @Override public View onCreateView(final String name, final Context context, final AttributeSet attrs) { if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) { try { // Ask our inflater to create the view final LayoutInflater f = getLayoutInflater(); final View[] view = new View[1]; try { view[0] = f.createView(name, null, attrs); } catch (InflateException e) { hackAndroid23(name, attrs, f, view); } // Kind of apply our own background new Handler().post(new Runnable() { public void run() { view[0].setBackgroundColor(Color.WHITE); } }); return view[0]; } catch (InflateException e) { } catch (ClassNotFoundException e) { } } return null; } }); } static void hackAndroid23(final String name, final android.util.AttributeSet attrs, final LayoutInflater f, final View[] view) { // mConstructorArgs[0] is only non-null during a running call to // inflate() // so we make a call to inflate() and inside that call our dully // XmlPullParser get's called // and inside that it will work to call // "f.createView( name, null, attrs );"! try { f.inflate(new XmlPullParser() { @Override public int next() throws XmlPullParserException, IOException { try { view[0] = (TextView) f.createView(name, null, attrs); } catch (InflateException e) { } catch (ClassNotFoundException e) { } throw new XmlPullParserException("exit"); } }, null, false); } catch (InflateException e1) { // "exit" ignored } } 
 protected void setMenuBackground() { getLayoutInflater().setFactory(new Factory() { @Override public View onCreateView (String name, Context context, AttributeSet attrs) { if (name.equalsIgnoreCase("com.android.internal.view.menu.IconMenuItemView")) { try { // Ask our inflater to create the view LayoutInflater f = getLayoutInflater(); final View view = f.createView(name, null, attrs); // Kind of apply our own background new Handler().post( new Runnable() { public void run () { view.setBackgroundResource(R.drawable.gray_gradient_background); } }); return view; } catch (InflateException e) { } catch (ClassNotFoundException e) { } } return null; } }); } 

Это файл XML

 gradient android:startColor="#AFAFAF" android:endColor="#000000" android:angle="270" shape 
  <style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:itemBackground">#000000</item> </style> 

Это отлично работает для меня

Intereting Posts
Не понимаю, как использовать GridLayout.spec () Скрыть групповой просмотр в ExpandableList Android PhoneGap приложение не размер содержимого, как эквивалент Chrome / браузер страницу делает Изменение размера карты во время выполнения Цифровая клавиатура Android EditText и разрешить текст Как начать новую деятельность с lockscreen? Как записывать аудио на веб-странице (iOS, Android, ПК / Mac) – без вспышки Могу ли я получить идентификатор устройства Android через мобильный сайт? Запуск PopupMenu с помощью кнопок внутри элемента ListView Значение, генерируемое с помощью случайной функции () Установка приложения phonegap в эмулятор Android с помощью окон CLI Как я могу создать локаль с обычным первым днем ​​недели? Как играть в видеофайл в андроиде? Как создать банку Android Library с градиентом без публичного раскрытия исходного кода? Загрузите изображение с помощью функции Parse Cloud Code