オプションメニューのデフォルトの色を白に変更しようとしています。オプションメニューのすべての項目の背景を黒にします。
menu要素内のitem要素でandroid:itemBackground = "#000000"のようないくつかの撮影を試みましたが、機能しませんでした。
どうすればこれを達成できますか?
オプションメニューのデフォルトの色を白に変更しようとしています。オプションメニューのすべての項目の背景を黒にします。
menu要素内のitem要素でandroid:itemBackground = "#000000"のようないくつかの撮影を試みましたが、機能しませんでした。
どうすればこれを達成できますか?
回答:
すべてのオプションを試すのにかなりの時間を費やした後、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までテスト済み。
これは明らかに多くのプログラマーが抱えている問題であり、Googleが満足できるサポートされたソリューションをまだ提供していない問題です。
このトピックに関する投稿には、さまざまな意図や誤解が浮かんでいるため、回答する前にこの回答全体をお読みください。
以下に、このページの他の回答からのハッキングのより「洗練された」コメント付きのバージョンを含め、これらの非常に密接に関連する質問からのアイデアを組み込んでいます:
Android:アプリケーションのメニューをカスタマイズします(例:背景色)
http://www.macadamian.com/blog/post/android_-_theming_the_unthemable/
Androidオプションメニューの背景を半透明にすることはできますか?
http://www.codeproject.com/KB/android/AndroidMenusMyWay.aspx
このハックを2.1(シミュレータ)、2.2(2つの実デバイス)、および2.3(2つの実デバイス)でテストしました。テストする3.Xタブレットはまだありませんが、必要な変更があればここに投稿します。ここで説明するように、3.Xタブレットはオプションメニューではなくアクションバーを使用するとします。
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
ソリューションの概要SO FAR:
いくつかのポスターは、LayoutInflater.Factoryを含むハックを提案しています。提案されたハックは、Android <= 2.2では機能し、Android 2.3では失敗しました。これは、ハックが文書化されていない仮定を行ったためです。現在同じLayoutInflaterインスタンスのLayoutInflater.inflate()の呼び出し内にいなくても、LayoutInflater.getView()を直接呼び出すことができます。Android 2.3の新しいコードはこの前提を破り、NullPointerExceptionを引き起こしました。
以下の少し洗練されたハックは、この仮定に依存していません。
さらに、ハッキングは、ドキュメント化されていない内部クラス名「com.android.internal.view.menu.IconMenuItemView」を(Javaタイプとしてではなく)文字列として使用することにも依存しています。これを回避する方法を私は見ていませんが、それでも指定された目標を達成します。ただし、「com.android.internal.view.menu.IconMenuItemView」が現在のシステムに表示されない場合にフォールバックする慎重な方法でハッキングを行うことは可能です。
繰り返しますが、これはハックであり、決してこれがすべてのプラットフォームで機能すると主張しているわけではありません。しかし、私たちの開発者は、すべてが本である必要がある幻想的なアカデミックな世界に住んでいるわけではありません。たとえば、3.Xタブレットでは、オプションメニューではなくアクションバーを使用するため、「com.android.internal.view.menu.IconMenuItemView」が存在する可能性は低いと思われます。
最後に、一部の開発者は、Androidオプションメニューを完全に抑制し、独自のメニュークラスを作成することでこの問題を解決しています(上記のリンクのいくつかを参照)。私はこれを試していませんが、独自のビューを記述してAndroidのビューを置き換える方法を理解する時間があれば(ここでは悪魔が詳細にあるはずです)、何も必要としない素晴らしいソリューションかもしれません文書化されていないハッキング。
ハック:
これがコードです。
このコードを使用するには、アクティビティonCreate()またはアクティビティonCreateOptionsMenu()からaddOptionsMenuHackerInflaterFactory()ONCEを呼び出します。オプションメニューの以降の作成に影響を与えるデフォルトファクトリを設定します。これは、すでに作成されているオプションメニューには影響しません(以前のハックでは、関数が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;
}
});
}
読んでいただきありがとうございます。
メニューの背景のスタイル属性はandroid:panelFullBackground
です。
ドキュメンテーションが言っていることにもかかわらず、それはリソースである必要があります(@android:color/black
またはなど@drawable/my_drawable
)、カラー値を直接使用するとクラッシュします。
これにより、primalpopのソリューションを使用して変更または削除できなかったアイテムの境界線も削除されます。
テキストの色については、スタイルを介して2.2で設定する方法が見つからなかったため、すべてを試したことを確信しています(これにより、メニューの背景属性を発見しました)。そのためには、primalpopのソリューションを使用する必要があります。
Android 2.3の場合、これは非常に重いハッキングで実行できます。
Android 2.3の問題の根本的な原因は、LayoutInflaterでmConstructorArgs [0] = mContextが、
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;
}
これが私の解決方法です。スタイルで背景色とテキスト色を指定したところです。つまり、res> values> styles.xmlファイルです。
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:itemBackground">#ffffff</item>
<item name="android:textColor">#000000</item>
</style>
他の多くの投稿と同じように、皆さんが問題を複雑にしすぎていることに注意してください。必要なのは、必要な背景を持つ描画可能なセレクターを作成し、実際のアイテムに設定することだけです。私は2時間だけあなたの解決策(このページで提案されている)を試してみましたが、どれもうまくいきませんでした。言うまでもなく、お使いの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マップをお試しください!
<style name="AppThemeLight" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:itemBackground">#000000</item>
</style>
これは私にとってはうまくいきます
/*
*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
任意の色を設定したい場合、これはでかなりうまくいくようですandroidx
。KitKatとPieでテストされています。これをあなたに入れてくださいAppCompatActivity
:
@Override public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
if (name.equals("androidx.appcompat.view.menu.ListMenuItemView") &&
parent.getParent() instanceof FrameLayout) {
((View) parent.getParent()).setBackgroundColor(yourFancyColor);
}
return super.onCreateView(parent, name, context, attrs);
}
これはの色を設定します。これは、ごandroid.widget.PopupWindow$PopupBackgroundView
想像のとおり、背景色を描画します。オーバードローはなく、半透明の色も使用できます。