在Android中,是否可以使MenuItem变为灰色(不仅仅是禁用)?

54

有一个与此相同功能的问题 在Blackberry上,还有几个不同的帖子提到了这个bug(据我所知,这个bug已经被关闭而没有解决),但我没有找到Android特定的帖子。

基于某些状态,我正在调用setEnabled(false)来禁用某些菜单项,但它们在视觉上看起来都一样。 我想以某种方式将它们偏移,以便用户知道当前选项不可用——有没有任何方法可以做到这一点?

9个回答

82
在所有Android版本中,最简单的方法是使用以下方式将菜单操作图标显示为禁用状态,并使其在功能上也被禁用:
@Override
public boolean onPrepareOptionsMenu(Menu menu) {

    MenuItem item = menu.findItem(R.id.menu_my_item);

    if (myItemShouldBeEnabled) {
        item.setEnabled(true);
        item.getIcon().setAlpha(255);
    } else {
        // disabled
        item.setEnabled(false);
        item.getIcon().setAlpha(130);
    }
}

2
这正是我正在寻找的。 - ElliotM
没错,这对我很有帮助,能够保留原始图标真不错。 - Dale Zak
19
注意:如果这个图标在其他地方使用,请确保在设置 alpha 值之前对其进行 mutate(),否则所有使用此可绘制对象的地方都会被调整,这不一定是您想要的。 - dimsuz
有没有其他方法来设置alpha值?(在样式内) - balaji
7
请注意,这仅适用于图标型 MenuItem。对于纯文本 MenuItem 无效。 - Ivan
与仅准备两种不同颜色的图标并交换它们相比,这个有多昂贵? - Ryuu

64

我有同样的问题。有两种方法可以使其工作:

  1. 将图标放入StateList中,这样就会在禁用时使用不同的图标。
  2. 现在我使用的方法是,在 onPrepareOptionsMenu() 中使用以下内容自己更改图标:

  3. public boolean onPrepareOptionsMenu(Menu menu) {
        boolean menusEnabled = reachedEndOfSlidehow(); // enable or disable?
        MenuItem item = menu.findItem(R.id.menu_next_slide);
        Drawable resIcon = getResources().getDrawable(R.drawable.ic_next_slide);
    
        if (!menusEnabled)
            resIcon.mutate().setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
    
        item.setEnabled(menusEnabled); // any text will be automatically disabled
        item.setIcon(resIcon);
    }
    
    你可以调用invalidateOptionsMenu() (或者使用ABS的supportInvalidateOptionsMenu()) 来重新构建菜单。 编辑: 更新解决方案2
    来源:https://groups.google.com/forum/?fromgroups#!topic/actionbarsherlock/Z8Ic8djq-3o

3
这实际上会改变菜单项文本/标题的颜色吗?也就是说,它会变成灰色吗?根据我的理解,这只会影响图标。如果你没有图标,只有文本怎么办? - Tony Chan
2
调用item.setEnabled()将使文本看起来变灰。问题在于,在操作栏上,文本被变灰了,但图标没有——这段代码会将其变灰。 - Oleg Vaskevich
19
Nobu Games在你提供的Google Groups帖子中的回复对我在Jelly Bean上非常有效:menuItem.getIcon().setAlpha(enabled ? 255 : 64); - poshaughnessy

13

我发现了一种使用可绘制选择器XML文件解决这个问题的新方法。您只需创建一个选择器,其中包含您想在菜单项中使用的图标,然后您可以更改位图的色调、透明度或两者都更改:

<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_enabled="true">
        <bitmap android:src="@drawable/ic_menu_item"
            android:tint="@color/enabled_color"
            android:alpha="@integer/enabled_alpha"/>
    </item>

    <item android:state_enabled="false">
        <bitmap android:src="@drawable/ic_menu_item"
            android:tint="@color/disabled_color"
            android:alpha="@integer/disabled_alpha"/>
    </item>
</selector>
作为一则旁注,我喜欢将着色设置为"?android:attr/textColorPrimary"以启用状态,而在禁用状态下设置为"?android:attr/textColorHint"。这样它就会根据使用的主题进行调整。
然后你可以在菜单xml文件中将图标设置为选择器资源。
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/menu_action"
        android:orderInCategory="0"
        android:title="@string/title_menu_action"
        android:icon="@drawable/ic_menu_item_selector"
        app:showAsAction="ifRoom"/>

</menu>

那么当您调用item.setEnabled(enabled)时,图标的颜色和/或透明度将随状态改变而改变!


1
此解决方案仅适用于 Android 5.0+(API 21+)棒棒糖及更高版本,因此在早期版本上需要使用 setColorFilter。 - straya
与仅准备两种不同颜色的图标并交换它们相比,这个有多昂贵? - Ryuu
@Ryuu 用户矢量可绘制对象和更改颜色,而不是两个PNG文件。 - Manikandan Selvanathan

4
我用的方法是在NavigationView中使用“itemIconTint”,您还可以通过使用“itemTextColor”将文本变灰。
这是NavigationView:
<android.support.design.widget.NavigationView
    android:id="@+id/nav_view"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:itemBackground="@color/white"
    android:background="@color/white"
    app:itemTextColor="@color/menu_text_color"
    app:itemIconTint="@color/menu_text_color"
    app:menu="@menu/main_drawer" />

"@color/menu_text_color"是一个选择器:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_checked="true" android:color="@color/primaryColor" />
    <item android:state_enabled="false" android:color="@color/disabled_text_color" />
    <item android:color="@color/primaryText" />
</selector>

最后,如果您想禁用菜单项,
MenuItem item = mNavigationView.getMenu().findItem(R.id.your_menu_item);
item.setEnabled(isEnable);

完成!


3
我在使用MaterialComponents主题的现代Android上遇到了困难。我的问题是我在styles.xml中设置了<item name="actionMenuTextColor">@color/blue</item>,这会覆盖文本颜色,无论该项目是否启用或禁用。解决方法是设置一个颜色状态列表而不是直接设置颜色。
我的样式属性现在看起来像这样: <item name="actionMenuTextColor">@color/menu_color_selector</item>

1
我遇到了完全相同的问题。谢谢! - Mariusz Wiazowski
我曾经遇到过同样的问题,但是 actionMenuTextColor 对我没有任何影响(我将其设置为粉色,但无处可见)。如果仍然有人在使用 Material 组件时遇到问题,请查看我的答案 https://dev59.com/D2kw5IYBdhLWcg3wyNoW#66248473。 - Chrispher

3

我曾经遇到这样的问题:文本和图标都无法在显示上明显改变。其他答案对我不起作用或者不够优雅。下面是一个可以解决最新Material recommendations的答案。

你可以简单地在onPrepareOptionsMenu(menu: Menu)中调用menu.findItem(R.id.menu_my_item).isEnabled = false

如果你需要onPrepareOptionsMenu再次运行,只需调用invalidateOptionsMenu()activity?.invalidateOptionsMenu()(从片段中调用),应用程序将会将菜单排队以重新创建。或者你可以将菜单项存储在成员变量中,以便稍后修改它,但要小心在onDestroyOptionsMenu中销毁对它的引用,以避免内存泄漏。

菜单项被禁用就足以自动使文本或图标变灰。困难在于设置样式使其工作。

简短回答

首先创建一个颜色状态列表my_color_state_list.xml,你希望你的图标和文本使用它(例如,启用时为黑色,禁用时为灰色)。(查看完整答案以获取示例。)

如果你正在使用com.google.android.material.appbar.MaterialToolbar,你可以通过提供自定义主题叠加层来告诉它使用这个选择器来为图标和文本着色。在活动的XML中,给工具栏设置属性android:theme="@style/Foo",并在某个地方定义该样式:

    <style name="Foo">
        <item name="colorControlNormal">@color/my_color_state_list</item>
        <item name="actionMenuTextColor">@color/my_color_state_list</item>
    </style>

现在,当菜单项通过menu.findItem(R.id.menu_my_item).isEnabled = false启用或禁用时,文本颜色将自动更改,使用颜色?attr/colorControlNormal的任何图标也将自动更改颜色。

完整回答

我的起点

我的菜单项是 Material 工具栏的一部分。这个回答可能对其他类型的工具栏/应用栏有帮助,但效果可能会有所不同。在我的活动中,我有以下内容:

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.MaterialComponents.Toolbar.Surface"/>

我正在使用的主题大致如下:

    <style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <item name="colorPrimary">@color/blue</item>
        <item name="colorSecondary">@color/green</item>
        <item name="colorSurface">@color/lightGrey</item>
        <item name="colorOnSurface">@color/black</item>
        [...]
        <item name="windowActionModeOverlay">true</item>
    </style>

通常情况下,在按钮和菜单项(以及任何地方)使用的图标默认颜色应为?attr/colorControlNormal。例如,我可能有一个矢量图像,看起来像这样:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="?attr/colorControlNormal"
    android:tintMode="src_atop">
  <path android:pathData="..." android:fillColor="@android:color/white"/>
</vector>

如果你从Material Icons下载图标,你会发现它们都使用colorControlNormal。
我需要做什么:
如果你回头看一下我的工具栏定义,你会发现它使用了一个ThemeOverlay ThemeOverlay.MaterialComponents.Toolbar.Surface,它被定义为:
<style name="ThemeOverlay.MaterialComponents.Toolbar.Surface" parent="">
    <item name="colorControlNormal">@color/material_on_surface_emphasis_medium</item>
    <item name="actionMenuTextColor">@color/material_on_surface_emphasis_medium</item>
</style>

这将菜单项的文本颜色和图标颜色设置为@color/material_on_surface_emphasis_medium,它不受启用或禁用的影响。@color/material_on_surface_emphasis_medium的外观如下:

<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:alpha="@dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
</selector>

您可能正在使用ThemeOverlay.MaterialComponents.Toolbar.Primary,它存在类似的问题 - 它仅使用colorOnPrimary

我们需要使用自己的颜色状态列表来响应启用状态。 因此,请创建一个名为res/color/menu_item_selector.xml的新文件,其内容类似于以下内容:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="true" android:alpha="@dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
    <item android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>

你看,我使用了材料库使用的相同约定来定义alpha值,我使用colorOnSurface作为我的颜色。如果您在使用ThemeOverlay.MaterialComponents.Toolbar.Primary,则应改用colorOnPrimary。当然,您可以在此处使用任何颜色或alpha值,这取决于您。

现在,在res/values/styles.xml中创建一个指向此选择器的新的ThemeOverlay,继承您正在使用的任何ThemeOverlay:

<!-- Toolbar - overrides the menu text color to use a selector that responds to whether it's enabled or not -->
    <style name="ThemeOverlay.MyTheme.Toolbar" parent="ThemeOverlay.MaterialComponents.Toolbar.Surface">
        <!-- Color used in the icons of menu actions (i.e. non-overflow menu items). This is just convention, this will affect anything that uses ?attr/colorControlNormal) -->
        <item name="colorControlNormal">@color/menu_item_color_selector</item>
        <!-- Color used in the text of menu actions (i.e. non-overflow menu items) -->
        <item name="actionMenuTextColor">@color/menu_item_color_selector</item>
    </style>

现在我们可以将ThemeOverlay应用于工具栏:

    <com.google.android.material.appbar.MaterialToolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.MyTheme.Toolbar"/>

2

setEnabled(false)API Level < 14 上运行良好,但在 14 上该项仍然可点击。


这个答案与问题相关吗? - Ryuu

1

请查看此 链接

setEnabled 也可用于 MenuItems


1
是的,我正在使用setEnabled,但它在视觉上没有改变。 - Waynn Lue
尝试下面的代码: public boolean onCreateOptionsMenu(Menu menu) { menu.add(0, 1, 1, "测试").setEnabled(false); return super.onCreateOptionsMenu(menu); } - Pavandroid
当你说它工作正常时,是指它将文本更改为灰色吗?还是只是禁用了?我正在尝试做第一件事。 - Waynn Lue
这将成为Android默认的禁用菜单项,它不可选择,普通的Android用户可以轻松识别和知道。 - Pavandroid
这是可能的,而且并不难 - 检查我的答案。 - Oleg Vaskevich
显示剩余3条评论

1
这是一种简单的方法(使用Kotlin):
fun changeMenuItemColour(enabled: Boolean) {
    var menuItem = SpannableString(mCustomToolbar?.menu?.findItem(R.id.some_menu_item)?.title)
    var style = activity?.resources?.getColor(R.color.darkGraphite)!!
    if (enabled) style = activity?.resources?.getColor(R.color.black)!!
    menuItem.setSpan(ForegroundColorSpan(style), 0, menuItem.length, 0)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接