如何为Android中选项菜单的文本着色?

时间:2014-10-11 06:38:36

标签: android android-layout android-resources

我有一个正确设置背景颜色的弹出主题,但不会更改文本颜色。我该怎么做?

<style name="PopupTheme" parent="@style/Widget.AppCompat.Light.ListPopupWindow">
    <item name="background">@color/green</item>
    <item name="android:popupBackground">@color/green</item>

    <!-- These are not being applied -->
    <item name="actionMenuTextColor">@color/white</item>
    <item name="android:actionMenuTextColor">@color/white</item>
    <item name="android:textColor">@color/white</item>
    <item name="android:foreground">@color/white</item>
    <item name="android:colorForeground">@color/white</item>
</style>

1 个答案:

答案 0 :(得分:0)

这显然是许多程序员所拥有的问题,Google尚未提供令人满意的支持解决方案。

在这个主题的帖子上有很多交叉的意图和误解,所以请在回复之前阅读整个答案。

下面我在本页的其他答案中加入了一个更“精致”且评论很好的黑客版本,同时也包含了这些非常密切相关问题的想法:

Change background color of android menu

How to change the background color of the options menu?

Android: customize application's menu (e.g background color)

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

Android MenuItem Toggle Button

Is it possible to make the Android options menu background non-translucent?

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

Setting the menu background to be opaque

我在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&lt; = 2.3开始就无法使用。因此,我们必须使用适用于当前版本的解决方案,旨在最大限度地降低未来版本中崩溃/破坏的可能性。如果必须失败,我们希望解决方案无法正常返回默认行为。

为什么人们可能需要控制选项菜单的外观(通常与应用程序其余部分的视觉样式相匹配)有很多正当理由,所以我不会详述。

发布了一个关于此问题的Google Android错误:请通过主演此错误添加您的支持(请注意Google不鼓励“我也是”评论:只需一颗星就足够了):

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

解决方案摘要:

有几张海报提出了涉及LayoutInflater.Factory的黑客攻击。建议的hack适用于Android&lt; = 2.2而Android 2.3失败,因为黑客做了一个没有记录的假设:可以直接调用LayoutInflater.getView()而不会在同一个LayoutInflater实例上调用LayoutInflater.inflate() 。 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平板电脑上,因为它们使用操作栏而不是选项菜单。

最后,一些开发人员通过完全抑制Android选项菜单并编写自己的菜单类(参见上面的一些链接)解决了这个问题。我没有试过这个,但是如果你有时间编写自己的View并想出如何替换Android的视图(我确定这里的细节中有魔鬼)那么它可能是一个不需要任何解决方案的好解决方案没有证件的黑客。

HACK:

这是代码。

要使用此代码,请从您的活动onCreate()或您的活动onCreateOptionsMenu()调用addOptionsMenuHackerInflaterFactory()ONCE。它设置了一个默认工厂,它将影响后续创建任何选项菜单。它不会影响已经创建的选项菜单(以前的hacks使用了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;
        }
    });
}

感谢阅读和享受!