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

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

我发现的唯一select是通过添加自定义样式来永久更改图标。 问题是,在未来的将来,我们将需要改变这在使用我们的应用程序。

我们的应用程序是对一系列在线平台的扩展,因此用户可以input其平台的url。 这些都有自己的风格,并会通过对应用程序的API调用来获取。

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

目前我更改操作栏中的其他图标,如下所示:

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); } 

现在我将不得不使用样式。

你实际上可以通过编程来改变溢出图标。 这是一个例子:

为溢出菜单创build样式并传入内容描述

 <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); } 

View.findViewsWithText被添加到API级别14,所以你必须使用自己的兼容性方法:

 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); } } } 

结果

例

Adneal的答案很好,直到最近我才使用它。 但后来我想我的应用程序使用材料devise,因此使用Theme.AppCompat.*风格和android.support.v7.widget.Toolbar

是的,它停止工作,我试图通过将Your.Theme的父母设置为@style/Widget.AppCompat.ActionButton.Overflow来解决它。 它通过propertyly设置contentDescription工作,但是当它投射到ImageButton时失败。 原来在最新(version 23) android.support.v7OverflowMenuButtonAppCompatImageView延伸 。 更改铸造类已经足够使其在运行棒棒糖的Nexus 5上的工具栏上工作。

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

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

那么为什么不使用它呢? 另外通过汉斯的想法(在评论中)我实现了监听器,以摆脱postDelayed延迟一些随机时间。 由于溢出图标已经在AppCompat库中,所以我也会使用它 – 我正在使用彩色滤镜,所以我不需要任何图标资源。

基于Adneal与Android棒棒糖改进工作的代码:

 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); } }); } 

和另一个StackOverflow答案 :

 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你应该溢出buttonTintImageView

作为支持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); } } 

有一个更简单的方法来改变溢出图标。 有一个例子,如何改变溢出图标的颜色,但你可以适应它来改变图像:

  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); } } 

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

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

中提琴!

基于@michalbrz的答案,我用下面来改变图标本身。 🙂

 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; } TintImageView overflow = (TintImageView) outViews.get(0); //overflow.setColorFilter(Color.CYAN); //changes color overflow.setImageResource(R.drawable.dots); removeOnGlobalLayoutListener(decorView, this); } }); 

使用appcompat-v7:23.0.1没有@adneal或@michalbrz为我工作。 我不得不改变@ 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的代码中挖了一点之后,我发现了如何获取图标的drawable(在setIcon()查找),然后我只是将cast更改为ActionMenuItemView ,将颜色filter应用于getCompoundDrawables()和Voila获得的左侧drawable。 有用!

我以一种简单的方式完成了这个工作,请看下面的代码片段:

 @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); } 

请让我知道这里是否有更多的解释。