以编程方式更改 Android 溢出菜单图标
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22046903/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Changing the Android Overflow menu icon programmatically
提问by Mathijs Segers
I've been looking for a method to programmatically change the color of the overflow menu icon in android.
我一直在寻找一种方法来以编程方式更改 android 中溢出菜单图标的颜色。
The only option I have found is to change the icon permanently by adding a custom style. The problem is that in the nearby future we will need to change this during the use of our app.
我发现的唯一选择是通过添加自定义样式来永久更改图标。问题是在不久的将来,我们将需要在使用我们的应用程序期间更改此设置。
Our app is an extension to a series of online-platforms and therefore a user can enter their platform's web-url. These have their own styles and will be fetched by an API call towards the app.
我们的应用程序是一系列在线平台的扩展,因此用户可以输入他们平台的 web-url。这些有自己的样式,并将通过对应用程序的 API 调用获取。
These might adress me to change the color of the icon...
这些可能会让我改变图标的颜色......
Currently I change other icons in the Actionbar like this:
目前我更改了操作栏中的其他图标,如下所示:
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);
}
For now I'll have to use the styles.
现在我将不得不使用这些样式。
回答by adneal
You actually can programmatically change the overflow icon using a little trick. Here's an example:
您实际上可以使用一个小技巧以编程方式更改溢出图标。下面是一个例子:
Create a style for the overflow menu and pass in a content description
为溢出菜单创建样式并传入内容描述
<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>
Now call ViewGroup.findViewsWithText
and pass in your content description. So, something like:
现在调用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
was added in API level 14, so you'll have to use your own compatibility method:
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);
}
}
}
Results
结果
回答by michalbrz
Adneal's answer is great and I was using it until recently. But then I wanted my app to make use of material design and thus Theme.AppCompat.*
style and android.support.v7.widget.Toolbar
.
Adneal 的回答很棒,直到最近我还在使用它。但后来我希望我的应用程序利用材料设计,从而使用Theme.AppCompat.*
样式和android.support.v7.widget.Toolbar
.
Yes, it stopped working and I was trying to fix it by setting Your.Theme
's parent to @style/Widget.AppCompat.ActionButton.Overflow
. It worked by propertly setting contentDescription
but then it failed when casting to ImageButton
. It turned out in latest (version 23) android.support.v7
class OverflowMenuButton
extends from AppCompatImageView
. Changing casting class was enought to make it work with Toolbar on Nexus 5 running Lollipop.
是的,它停止工作了,我试图通过将Your.Theme
的父级设置为@style/Widget.AppCompat.ActionButton.Overflow
. 它通过正确设置工作,contentDescription
但在转换为ImageButton
. 事实证明,最新的(version 23) android.support.v7
课程OverflowMenuButton
从AppCompatImageView
. 更改转换类足以使其与运行 Lollipop 的 Nexus 5 上的工具栏一起使用。
Then I ran it on Galaxy S4 with KitKat and no matter what I tried I couldn't set overflow's contentDescription
to my custom value. But in AppCompat stylesI found it already has default value:
然后我用 KitKat 在 Galaxy S4 上运行它,无论我尝试什么,我都无法将溢出设置contentDescription
为我的自定义值。但是在AppCompat 样式中,我发现它已经具有默认值:
<item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>
So why not use it? Also by Hannes idea (in comments) I implemented listener, to get rid of some random time for delay in postDelayed
. And as overflow icon is already in AppCompat library, then I would use it as well - I am applying color filter, so I don't need any icon resource on my own.
那么为什么不使用它呢?同样通过 Hannes 的想法(在评论中)我实现了监听器,以摆脱postDelayed
. 由于溢出图标已经在 AppCompat 库中,所以我也会使用它 - 我正在应用颜色过滤器,所以我自己不需要任何图标资源。
My code based on Adneal's work with Android Lollipop improvements:
我的代码基于 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);
}
});
}
and as per 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);
}
}
of course instead of Color.CYAN
you can use your own color - activity.getResources().getColor(R.color.black);
当然,Color.CYAN
您可以使用自己的颜色代替-activity.getResources().getColor(R.color.black);
EDIT: Added support for latest AppCompat library (23), which uses AppCompatImageView For AppCompat 22 you should cast overflow button to TintImageView
编辑:添加了对最新 AppCompat 库 (23) 的支持,它使用 AppCompatImageView 对于 AppCompat 22 你应该将溢出按钮投射到 TintImageView
回答by Lorne Laliberte
As of support 23.1 Toolbar now has getOverflowIcon()and setOverflowIcon()methods, so we can do this much more easily:
支持 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);
}
}
回答by Jacek Kwiecień
There is much better solution. You can do it programmatically in the runtime
有更好的解决方案。您可以在运行时以编程方式执行此操作
toolbar.overflowIcon?.setColorFilter(colorInt, PorterDuff.Mode.SRC_ATOP)
Viola!
中提琴!
回答by artem
There is the less-hacky solution for changing the overflow icon. There is an example how to change overflow icon's color, but you can adapt it to change the image:
有一个更简单的解决方案来改变溢出图标。有一个如何更改溢出图标颜色的示例,但您可以对其进行调整以更改图像:
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);
}
}
回答by Deva
Guys I've done this in a simple way, Please look on this snippet as follows:
伙计们,我以一种简单的方式完成了此操作,请按以下方式查看此代码段:
@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);
}
Please let me know if any more clarification here.
如果有更多说明,请告诉我。
回答by SUMIT MONAPARA
Don't need to create the new style resource, just use setOvewflowIcon(drawable) method to the toolbar object and pass the drawable that you want to use as icon
不需要创建新的样式资源,只需使用 setOvewflowIcon(drawable) 方法到工具栏对象并将要用作图标的可绘制对象传递
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
toolbar.setOverflowIcon(getResources().getDrawable(R.drawable.ic_notifications_black_24dp));
回答by Atul O Holic
Based on @michalbrz answer, I used the below to change the icon itself. :)
根据@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);
}
});
回答by MiguelHincapieC
Using appcompat-v7:23.0.1none of @adneal or @michalbrz worked for me. I had to change 2 lines of code of @michalbrz's answer to make it works.
I'm adding an answer because both current answers can be useful for someone, but if you are using last appcompat version like me you should use this one based on @michalbrz:
使用appcompat-v7:23.0.1,@adneal 或 @michalbrz 都不适合我。我不得不更改@michalbrz 的答案的 2 行代码才能使其正常工作。
我正在添加一个答案,因为当前的两个答案都可能对某人有用,但是如果您使用的是像我这样的最后一个 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);
}
});
}
Using michalbrz code I was getting this error:
使用 michalbrz 代码我收到此错误:
java.lang.ClassCastException: android.support.v7.internal.view.menu.ActionMenuItemView cannot be cast to android.support.v7.internal.widget.TintImageView
So, after digging a little in ActionMenuItemView
's code, I found how to get the icon's drawable (looking in setIcon()
), then I just changed casting to ActionMenuItemView
, applied color filter to left drawable got from getCompoundDrawables()
and Voila! it works!
因此,在深入研究了一些ActionMenuItemView
代码之后,我找到了如何获取图标的可绘制对象(在 中查找setIcon()
),然后我只是将转换更改为ActionMenuItemView
,将颜色过滤器应用于左侧可绘制对象getCompoundDrawables()
,瞧!有用!