Programmgesteuertes Ändern des Symbols für das Android-Überlaufmenü

Lesezeit: 10 Minuten

Ich habe nach einer Methode gesucht, um die Farbe des Überlaufmenüsymbols in Android programmgesteuert zu ändern.

Die einzige Option, die ich gefunden habe, besteht darin, das Symbol dauerhaft zu ändern, indem Sie einen benutzerdefinierten Stil hinzufügen. Das Problem ist, dass wir dies in naher Zukunft während der Nutzung unserer App ändern müssen.

Unsere App ist eine Erweiterung einer Reihe von Online-Plattformen und daher kann ein Benutzer die Web-URL seiner Plattform eingeben. Diese haben ihre eigenen Stile und werden von einem API-Aufruf an die App abgerufen.

Diese könnten mich auffordern, die Farbe des Symbols zu ändern …

Derzeit ändere ich andere Symbole in der Actionbar wie folgt:

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

Im Moment muss ich die Stile verwenden.

  • Ich glaube, der einzige Weg ist ein benutzerdefinierter Stil. Ich glaube nicht, dass Sie es programmgesteuert ändern können.

    – Robby Pond

    26. Februar 14 um 16:13 Uhr

  • Ich habe irgendwie das gleiche Gefühl, hoffe nur, dass ich falsch liege.

    – Mathijs Segers

    26. Februar 14 um 20:01 Uhr

Programmgesteuertes Andern des Symbols fur das Android Uberlaufmenu
Nebenniere

Sie können das Überlaufsymbol tatsächlich mit einem kleinen Trick programmgesteuert ändern. Hier ist ein Beispiel:

Erstellen Sie einen Stil für das Überlaufmenü und übergeben Sie eine Inhaltsbeschreibung

<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>

Ruf jetzt an ViewGroup.findViewsWithText und geben Sie Ihre Inhaltsbeschreibung ein. Also so etwas wie:

@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 wurde in API-Ebene 14 hinzugefügt, daher müssen Sie Ihre eigene Kompatibilitätsmethode verwenden:

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

Ergebnisse

Beispiel

  • @MathijsSegers Ich habe meine Antwort bearbeitet und kürzlich festgestellt, dass Sie sie dynamisch ändern können.

    – Nebenniere

    4. Juni 14 um 12:51 Uhr

  • Interessant, ich werde mich damit befassen, aber ich weiß nicht, ob ich es jemals brauchen werde, aber die Chancen stehen gut, dass ich es brauche. Danke!

    – Mathijs Segers

    5. Juni 14 um 8:17 Uhr

  • Anstelle des Timeouts könnte man wahrscheinlich auch a verwenden ViewTreeObserver.OnPreDrawListener: stackoverflow.com/questions/4393612/…

    – Hannes Struß

    24. November 14 um 14:05 Uhr

  • So eine schlechte Programmierung, die eine verzögerte Methode aufruft (in 1 Sekunde)

    – Benutzer25

    2. Februar 19 um 19:39 Uhr

Programmgesteuertes Andern des Symbols fur das Android Uberlaufmenu
michalbrz

Die Antwort von Adneal ist großartig und ich habe sie bis vor kurzem verwendet. Aber dann wollte ich, dass meine App Materialdesign nutzt und damit Theme.AppCompat.* Stil und android.support.v7.widget.Toolbar.

Ja, es funktionierte nicht mehr und ich versuchte, es durch Einstellung zu beheben Your.Theme‘s Eltern zu @style/Widget.AppCompat.ActionButton.Overflow. Mit richtiger Einstellung hat es funktioniert contentDescription aber dann scheiterte es beim Casting an ImageButton. Es stellte sich heraus in neusten (version 23) android.support.v7Klasse OverflowMenuButton erstreckt sich von AppCompatImageView. Das Ändern der Casting-Klasse reichte aus, damit es mit der Toolbar auf Nexus 5 mit Lollipop funktioniert.

Dann habe ich es auf Galaxy S4 mit KitKat ausgeführt und egal was ich versucht habe, ich konnte keine Überläufe einstellen contentDescription zu meinem benutzerdefinierten Wert. Aber in AppCompat-Stile Ich habe festgestellt, dass es bereits einen Standardwert hat:

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

Warum also nicht verwenden? Auch durch Hannes Idee (in den Kommentaren) habe ich Listener implementiert, um eine zufällige Zeit für die Verzögerung loszuwerden postDelayed. Und da sich das Überlaufsymbol bereits in der AppCompat-Bibliothek befindet, würde ich es auch verwenden – ich wende einen Farbfilter an, sodass ich keine eigenen Symbolressourcen benötige.

Mein Code basiert auf Adneals Arbeit mit Verbesserungen von 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);
        }
    });
}

und gemäß einer anderen StackOverflow-Antwort:

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

natürlich statt Color.CYAN Sie können Ihre eigene Farbe verwenden – activity.getResources().getColor(R.color.black);

BEARBEITEN: Unterstützung für die neueste AppCompat-Bibliothek (23) hinzugefügt, die AppCompatImageView verwendet. Für AppCompat 22 sollten Sie die Überlaufschaltfläche in TintImageView umwandeln

  • Brillant! Danke 🙂

    – Petrus

    13. Mai 16 um 8:58 Uhr

1644071648 483 Programmgesteuertes Andern des Symbols fur das Android Uberlaufmenu
Lorne Laliberte

Ab Support 23.1 hat die Toolbar nun getOverflowIcon() und setOverflowIcon() Methoden, damit wir das viel einfacher machen können:

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

  • Ich habe auf 23.1 aktualisiert und Ihren Code ausprobiert, aber er funktioniert bei mir nicht: S. Irgendwelche zusätzlichen Ratschläge oder Hilfen, um es zu erreichen?

    – MiguelHincapieC

    5. November 15 um 21:14 Uhr

  • Hmmm … Ich habe es auf mehreren Geräten mit 6.0, 5.0.1 und 4.4.4 getestet. Ich habe targetSdkVersion auf 23 gesetzt.

    – Lorne Laliberte

    5. November 15 um 21:39 Uhr

  • Versucht, was Sie vorschlagen (in meiner Antwort) und dieses auf 4.2.2-Gerät ohne Erfolg … was ich vermisse O_O ‘

    – MiguelHincapieC

    5. November 15 um 21:56 Uhr

  • @ 0mahc0 Sie haben auf 23.1 aktualisiert, aber haben Sie auch Ihre Compileroptionen/targetSdkVersion auf 23 gesetzt? Ich habe tatsächlich appcompat und andere Bibliotheken, die auf 23 statt 19 bauen (indem ich die Ziele in ihren project.properties, Build-Einstellungen usw. ändere). Abgesehen davon, sind Sie sicher, dass Sie tatsächlich die Symbolleiste in Ihrem Layout verwenden und nicht die Support-Aktionsleiste? (Wenn Sie mit altem Code arbeiten, ist es möglich, dass die Aktivität immer noch die Aktionsleiste verwendet, wobei eine Symbolleiste definiert ist, aber nicht verwendet wird. Welche Sie tatsächlich zur Laufzeit sehen, hängt davon ab, was Sie der Aktivität im Code zuweisen.)

    – Lorne Laliberte

    17. November 15 um 22:35 Uhr

  • Ich schätze, Sie haben Recht, ich werde all diese Dinge aktualisieren.

    – MiguelHincapieC

    18. November 15 um 14:42 Uhr

Es gibt viel bessere Lösung. Sie können dies programmgesteuert in der Laufzeit tun

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

Viola!

Es gibt die weniger hackige Lösung zum Ändern des Überlaufsymbols. Es gibt ein Beispiel zum Ändern der Farbe des Überlaufsymbols, aber Sie können es anpassen, um das Bild zu ändern:

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

1644071649 900 Programmgesteuertes Andern des Symbols fur das Android Uberlaufmenu
Atul O Holic

Basierend auf der Antwort von @michalbrz habe ich das Symbol unten verwendet, um das Symbol selbst zu ändern. 🙂

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

1644071649 982 Programmgesteuertes Andern des Symbols fur das Android Uberlaufmenu
MiguelHincapieC

Verwenden appcompat-v7:23.0.1 keiner von @adneal oder @michalbrz hat bei mir funktioniert. Ich musste 2 Codezeilen der Antwort von @michalbrz ändern, damit es funktioniert.

Ich füge eine Antwort hinzu, weil beide aktuellen Antworten für jemanden nützlich sein können, aber wenn Sie die letzte appcompat-Version wie ich verwenden, sollten Sie diese basierend auf @michalbrz verwenden:

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

Mit michalbrz-Code bekam ich diesen Fehler:

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

Also, nachdem ich ein wenig gegraben habe ActionMenuItemView‘s Code habe ich herausgefunden, wie man das drawable des Symbols erhält (indem ich in setIcon()), dann habe ich einfach Casting zu geändert ActionMenuItemViewangewandter Farbfilter nach links ziehbar erhalten getCompoundDrawables() und Voila! Es klappt!

  • Macht Sinn, da es sich um eine neuere Support-Bibliothek handelt, die andere Dinge als die ursprüngliche Frage erledigt. Aber das könnte sich für viele als wertvoll erweisen.

    – Mathijs Segers

    17. September 15 um 6:23 Uhr

  • Dies funktioniert ab 23.1 (wieder einmal!) nicht, Sie müssen umwandeln ImageView und verwenden overflow.setColorFilter(colorFilter) stattdessen. (Die Schaltfläche ist jetzt als ActionMenuPresenter.OverflowMenuButton aber das hat einen privaten Zugang.)

    – Lorne Laliberte

    4. November 15 um 22:48 Uhr

  • OMG!! Sie ändern es so sehr: S, ich werde versuchen, die Antwort zu aktualisieren (oder sie zu ändern, wenn Sie möchten)

    – MiguelHincapieC

    4. November 15 um 22:53 Uhr

  • Eigentlich gibt es ab 23.1 einen einfacheren Weg, dies zu tun, da sie getOverflowIcon() und setOverflowIcon() hinzugefügt haben – aber trotzdem habe ich Ihre Antwort positiv bewertet, da ich sie hilfreich fand, solange sie anhielt. 🙂

    – Lorne Laliberte

    4. November 15 um 23:32 Uhr

  • Thx @Lorne Laliberte, ich versuche es mit toolbar.getOverflowIcon().setColorFilter() aber es funktioniert nicht, können Sie mir einen Rat geben?. Ich habe übrigens auf 23.1 aktualisiert und mein Code funktioniert immer noch mit einer Warnung über eine private String-Ressource …. NVM Ich habe gerade Ihre Antwort bemerkt, hahaha;)

    – MiguelHincapieC

    5. November 15 um 20:55 Uhr


.

784270cookie-checkProgrammgesteuertes Ändern des Symbols für das Android-Überlaufmenü

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy