以编程方式更改Android Overflow菜单图标

时间:2014-02-26 16:09:14

标签: android android-actionbar

我一直在寻找一种方法来以编程方式更改android中溢出菜单图标的颜色。

我找到的唯一选择是通过添加自定义样式永久更改图标。 问题是,在不久的将来,我们需要在使用我们的应用程序时更改此功能。

我们的应用程序是一系列在线平台的扩展,因此用户可以进入其平台的web-url。它们有自己的样式,并且会通过API调用来获取应用程序。

这些可能会让我改变图标的​​颜色......

目前我在Actionbar中更改了其他图标:

if (ib != null){
            Drawable resIcon = getResources().getDrawable(R.drawable.navigation_refresh);
            resIcon.mutate().setColorFilter(StyleClass.getColor("color_navigation_icon_overlay"), PorterDuff.Mode.SRC_ATOP);
            ib.setIcon(resIcon);
}

现在我必须使用这些样式。

10 个答案:

答案 0 :(得分:32)

您实际上可以使用一个小技巧以编程方式更改溢出图标。这是一个例子:

为溢​​出菜单创建样式并传入内容说明

<style name="Widget.ActionButton.Overflow" parent="@android:style/Widget.Holo.ActionButton.Overflow">
    <item name="android:contentDescription">@string/accessibility_overflow</item>
</style>

<style name="Your.Theme" parent="@android:style/Theme.Holo.Light.DarkActionBar">
    <item name="android:actionOverflowButtonStyle">@style/Widget.ActionButton.Overflow</item>
</style>

现在请致电ViewGroup.findViewsWithText并传递您的内容说明。所以,像:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // The content description used to locate the overflow button
    final String overflowDesc = getString(R.string.accessibility_overflow);
    // The top-level window
    final ViewGroup decor = (ViewGroup) getWindow().getDecorView();
    // Wait a moment to ensure the overflow button can be located
    decor.postDelayed(new Runnable() {

        @Override
        public void run() {
            // The List that contains the matching views
            final ArrayList<View> outViews = new ArrayList<>();
            // Traverse the view-hierarchy and locate the overflow button
            decor.findViewsWithText(outViews, overflowDesc,
                    View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
            // Guard against any errors
            if (outViews.isEmpty()) {
                return;
            }
            // Do something with the view
            final ImageButton overflow = (ImageButton) outViews.get(0);
            overflow.setImageResource(R.drawable.ic_action_overflow_round_red);

        }

    }, 1000);
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Add a dummy item to the overflow menu
    menu.add("Overflow");
    return super.onCreateOptionsMenu(menu);
}
在API级别14中添加了

View.findViewsWithText,因此您必须使用自己的兼容性方法:

static void findViewsWithText(List<View> outViews, ViewGroup parent, String targetDescription) {
    if (parent == null || TextUtils.isEmpty(targetDescription)) {
        return;
    }
    final int count = parent.getChildCount();
    for (int i = 0; i < count; i++) {
        final View child = parent.getChildAt(i);
        final CharSequence desc = child.getContentDescription();
        if (!TextUtils.isEmpty(desc) && targetDescription.equals(desc.toString())) {
            outViews.add(child);
        } else if (child instanceof ViewGroup && child.getVisibility() == View.VISIBLE) {
            findViewsWithText(outViews, (ViewGroup) child, targetDescription);
        }
    }
}

<强>结果

Example

答案 1 :(得分:20)

Adneal的答案很棒,直到最近才使用它。但后来我想让我的应用程序使用材料设计,从而Theme.AppCompat.*样式和android.support.v7.widget.Toolbar

是的,它停止了工作,我试图通过将Your.Theme的父级设置为@style/Widget.AppCompat.ActionButton.Overflow来解决问题。它通过设置contentDescription进行设置,但在转换为ImageButton时失败了。事实证明,最新的(version 23) android.support.v7班级OverflowMenuButton中的来自AppCompatImageView 。改变铸造类是为了使其与运行Lollipop的Nexus 5上的工具栏配合使用。

然后我用KitKat在Galaxy S4上运行它,无论我尝试了什么,我都无法将溢出contentDescription设置为我的自定义值。但在AppCompat styles中我发现它已经有默认值:

<item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>

那么为什么不使用呢?同样由Hannes的想法(在评论中)我实现了监听器,以消除postDelayed中延迟的一些随机时间。由于溢出图标已经在AppCompat库中,我也会使用它 - 我正在应用滤色器,因此我自己不需要任何图标资源。

我的代码基于Adneal与Android Lollipop的改进工作:

public static void setOverflowButtonColor(final Activity activity) {
    final String overflowDescription = activity.getString(R.string.abc_action_menu_overflow_description);
    final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
    final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
    viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            final ArrayList<View> outViews = new ArrayList<View>();
            decorView.findViewsWithText(outViews, overflowDescription,
                    View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
            if (outViews.isEmpty()) {
                return;
            }
            AppCompatImageView overflow=(AppCompatImageView) outViews.get(0);
            overflow.setColorFilter(Color.CYAN);
            removeOnGlobalLayoutListener(decorView,this);
        }
    });
}

并按照another StackOverflow answer

public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
        v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
    }
    else {
        v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
    }
}

当然代替Color.CYAN,您可以使用自己的颜色 - activity.getResources().getColor(R.color.black);

编辑: 添加了对最新AppCompat库(23)的支持,该库使用AppCompatImageView 对于AppCompat 22,您应该将溢出按钮强制转换为TintImageView

答案 2 :(得分:18)

截至支持23.1工具栏现在有getOverflowIcon()setOverflowIcon()方法,因此我们可以更轻松地完成此任务:

public static void setOverflowButtonColor(final Toolbar toolbar, final int color) {
    Drawable drawable = toolbar.getOverflowIcon();
    if(drawable != null) {
        drawable = DrawableCompat.wrap(drawable);
        DrawableCompat.setTint(drawable.mutate(), color);
        toolbar.setOverflowIcon(drawable);
    }
}

答案 3 :(得分:7)

更改溢出图标的解决方案较少。有一个示例如何更改溢出图标的颜色,但您可以调整它以更改图像:

 private void setOverflowIconColor(int color) {
        Drawable overflowIcon = toolbar.getOverflowIcon();

        if (overflowIcon != null) {
            Drawable newIcon = overflowIcon.mutate();
            newIcon.setColorFilter(color, PorterDuff.Mode.MULTIPLY);
            toolbar.setOverflowIcon(newIcon);
        }
    }

答案 4 :(得分:7)

有更好的解决方案。您可以在运行时以编程方式执行此操作

toolbar.overflowIcon?.setColorFilter(colorInt, PorterDuff.Mode.SRC_ATOP)

中提琴!

答案 5 :(得分:1)

基于@michalbrz的答案,我使用下面的内容来更改图标本身。 :)

"\n"

答案 6 :(得分:1)

使用 appcompat-v7:23.0.1 @adneal或@michalbrz都没有为我工作。我不得不更改2行代码@ michalbrz的答案,以使其有效。

我添加了一个答案,因为当前的答案对某些人都很有用,但如果你使用像我这样的最后一个appcompat版本,你应该使用基于@michalbrz的这个答案:

private static void setOverflowButtonColor(final AppCompatActivity activity, final PorterDuffColorFilter colorFilter) {
    final String overflowDescription = activity.getString(R.string.abc_action_menu_overflow_description);
    final ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
    final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
    viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            final ArrayList<View> outViews = new ArrayList<>();
            decorView.findViewsWithText(outViews, overflowDescription,
                    View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
            if (outViews.isEmpty()) {
                return;
            }
            ActionMenuItemView overflow = (ActionMenuItemView)outViews.get(0);
            overflow.getCompoundDrawables()[0].setColorFilter(colorFilter);
            removeOnGlobalLayoutListener(decorView,this);
        }
    });
}

使用michalbrz代码我收到了这个错误:

java.lang.ClassCastException: android.support.v7.internal.view.menu.ActionMenuItemView cannot be cast to android.support.v7.internal.widget.TintImageView

所以,在ActionMenuItemView的代码中挖了一点之后,我发现了如何获得图标的可绘制(查看setIcon()),然后我就改变了ActionMenuItemView,应用了来自getCompoundDrawables()和Voila的左侧绘图的滤镜!它有效!

答案 7 :(得分:0)

伙计我以简单的方式完成了这项工作,请按以下方式查看此片段:

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_dashboard, menu);
        MenuItem item = menu.findItem(R.id.help);
        Drawable drawable = item.getIcon();
        drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);
        return super.onCreateOptionsMenu(menu);
    }

如果有任何更多说明,请告诉我。

答案 8 :(得分:0)

不需要创建新样式资源,只需对工具栏对象使用setOvewflowIcon(drawable)方法,然后将要用作图标的drawable传递给

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
       toolbar.setOverflowIcon(getResources().getDrawable(R.drawable.ic_notifications_black_24dp));

答案 9 :(得分:0)

toolBar.overflowIcon?.setTint(Color.WHITE)

在科特林中, 改变你想要的任何颜色:)