Изменение значка меню "Переполнение Android" программно
Я искал способ программного изменения цвета значка меню переполнения в android.
Единственный вариант, который я нашел, - это изменить значок навсегда, добавив собственный стиль.
Проблема в том, что в ближайшем будущем нам нужно будет изменить это во время использования нашего приложения.
Наше приложение является расширением серии онлайн-платформ, и поэтому пользователь может ввести свой веб-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);
}
Теперь мне придется использовать стили.
Ответы
Ответ 1
Фактически вы можете программно изменить значок переполнения, используя небольшой трюк. Вот пример:
Создать стиль для меню переполнения и передать описание содержимого
<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);
}
}
}
Результаты
![Example]()
Ответ 2
Аднеальный ответ велик, и я использовал его до недавнего времени. Но тогда я хотел, чтобы мое приложение использовало материальный дизайн и, таким образом, стиль Theme.AppCompat.*
и android.support.v7.widget.Toolbar
.
Да, он перестает работать, и я пытался его исправить, установив Your.Theme
parent на @style/Widget.AppCompat.ActionButton.Overflow
. Он работал путем правильной установки contentDescription
, но затем он не удался при нажатии на ImageButton
. Оказалось, что в последнем (version 23) android.support.v7
классе OverflowMenuButton
выходит из AppCompatImageView
. Изменение класса литья было достаточно, чтобы заставить его работать с Панели инструментов на Nexus 5, работающей с Lollipop.
Затем я запустил его на Galaxy S4 с KitKat, и независимо от того, что я пытался, я не мог установить переполнение contentDescription
в свое пользовательское значение. Но в стилях AppCompat Я обнаружил, что уже имеет значение по умолчанию:
<item name="android:contentDescription">@string/abc_action_menu_overflow_description</item>
Так почему бы не использовать его? Также по идее Ханнеса (в комментариях) я реализовал прослушиватель, чтобы избавиться от некоторого случайного времени задержки в 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);
}
});
}
и в соответствии с fooobar.com/questions/20225/...:
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);
EDIT:
Добавлена поддержка последней библиотеки AppCompat (23), которая использует AppCompatImageView
Для AppCompat 22 вы должны использовать кнопку переполнения для TintImageView
Ответ 3
В качестве поддержки 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);
}
}
Ответ 4
Существует менее хеш-решение для изменения значка переполнения. Существует пример того, как изменить цвет значка переполнения, но вы можете адаптировать его для изменения изображения:
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);
}
}
Ответ 5
Существует гораздо лучшее решение. Вы можете сделать это программно во время выполнения
toolbar.overflowIcon?.setColorFilter(colorInt, PorterDuff.Mode.SRC_ATOP)
Viola!
Ответ 6
Основываясь на ответе @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);
}
});
Ответ 7
Используя 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
, примененный цветовой фильтр, чтобы левый drawable получил от getCompoundDrawables()
и Voila! он работает!
Ответ 8
Ребята, я сделал это простым способом. Посмотрите на этот фрагмент следующим образом:
@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);
}
Пожалуйста, дайте мне знать, если есть какие-либо разъяснения здесь.