Modification de l'icône du menu Overflow Android par programme

J'ai cherché une méthode pour modifier par programmation la couleur de l'icône du menu de débordement dans Android.

La seule option que j'ai trouvée est de changer définitivement l'icône en ajoutant un style personnalisé. Le problème est que dans l'avenir proche, nous devrons changer cela lors de l'utilisation de notre application.

  • Hdpi ldpi mdpi icon / menu resolution
  • Android Studio pour chaque macro
  • Régler l'enfant relatif pour nettoyer l'espace inutilisé
  • Comment ajouter une boîte de message avec le bouton OK
  • Meilleures pratiques pour "couche de données" dans les applications client Android
  • Android: Comment vérifier le courrier électronique à un moment précis avec AlarmManager?
  • Notre application est une extension d'une série de plates-formes en ligne et, par conséquent, un utilisateur peut entrer dans l'url de leur plate-forme. Ils ont leurs propres styles et seront récupérés par un appel API vers l'application.

    Cela pourrait m'autoriser à modifier la couleur de l'icône …

    Actuellement, je change d'autres icônes dans la barre d'action comme ceci:

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

    Pour l'instant, je devrai utiliser les styles.

  • Mettre en œuvre une sécurité de niveau signature sur les services Android avec plus d'une signature autorisée
  • Modification des valeurs d'étape dans seekbar?
  • Impossible de synchroniser le projet Gradle
  • Le remplacement d'une chaîne dans AndroidManifest.xml pour un buildvariant ne fonctionne pas pour Gradle Version du plugin Android> 0.5.4
  • Android: cliquez sur l'événement pour la notification de la barre d'état
  • EditText se bloque après l'animation et en vie sur le défilement ...?
  • 8 Solutions collect form web for “Modification de l'icône du menu Overflow Android par programme”

    Vous pouvez réellement modifier l'icône de débordement par programme en utilisant un petit tour. Voici un exemple:

    Créez un style pour le menu débordement et passez une description de contenu

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

    Maintenant, appelez ViewGroup.findViewsWithText et passez la description de votre contenu. Donc, quelque chose comme:

     @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 été ajouté au niveau API 14, donc vous devrez utiliser votre propre méthode de compatibilité:

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

    Résultats

    Exemple

    La réponse d'Adneal est géniale et je l'utilisais jusqu'à récemment. Mais alors, je voulais que mon application utilise la conception matérielle et donc le style Theme.AppCompat.* Et android.support.v7.widget.Toolbar .

    Oui, cela a cessé de fonctionner et j'essayais de le résoudre en configurant le parent de Your.Theme sur @style/Widget.AppCompat.ActionButton.Overflow . Cela a fonctionné en configurant contentDescription mais il a échoué lors de la ImageButton de ImageButton . Il s'est avéré dans la dernière (version 23) android.support.v7 classe OverflowMenuButton s'étend de AppCompatImageView . La modification de la classe de casting était suffisante pour fonctionner avec la barre d'outils sur Nexus 5 exécutant Lollipop.

    Ensuite, je l'ai exécuté sur Galaxy S4 avec KitKat et peu importe ce que j'ai essayé, je ne pouvais pas définir le contentDescription du contentDescription à la valeur personnalisée. Mais dans les styles AppCompat, j'ai trouvé qu'il avait déjà une valeur par défaut:

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

    Alors pourquoi ne pas l'utiliser? Aussi par l'idée de Hannes (dans les commentaires), j'ai implémenté l'auditeur, pour éliminer un temps aléatoire pour le retard dans postDelayed . Et comme l'icône de débordement est déjà dans la bibliothèque AppCompat, je l'utiliserais aussi – J'applique un filtre couleur, donc je n'ai besoin d'aucune ressource icon pour moi-même.

    Mon code basé sur le travail d'Adneal avec les améliorations de 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); } }); } 

    Et selon une autre réponse 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); } } 

    Bien sûr, au lieu de Color.CYAN vous pouvez utiliser votre propre couleur – activity.getResources().getColor(R.color.black);

    EDIT: Ajout de la prise en charge de la dernière bibliothèque AppCompat (23), qui utilise AppCompatImageView For AppCompat 22, vous devez lancer le bouton de débordement sur TintImageView

    À partir de la barre d'outils de support 23.1, les méthodes getOverflowIcon () et setOverflowIcon () sont donc compatibles , de sorte que nous pouvons le faire beaucoup plus facilement:

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

    Il y a la solution moins dangereuse pour changer l'icône de débordement. Il existe un exemple de la façon de changer la couleur de l'icône de débordement, mais vous pouvez l'adapter pour changer l'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); } } 

    Sur la base de la réponse @michalbrz, j'ai utilisé ce qui suit pour changer l'icône elle-même. 🙂

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

    À l'aide d' appcompat-v7: 23.0.1 aucun de @adneal ou @michalbrz n'a fonctionné pour moi. J'ai dû changer 2 lignes de code de la réponse de @michalbrz pour le faire fonctionner.

    J'ajoute une réponse parce que les deux réponses actuelles peuvent être utiles à quelqu'un, mais si vous utilisez la dernière version d'appcompat comme moi, vous devriez utiliser celle-ci basée sur @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); } }); } 

    À l'aide du code michalbrz, j'avais cette erreur:

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

    Alors, après avoir creusé un peu dans le code de ActionMenuItemView , j'ai trouvé comment obtenir l'icône de l'icône (en regardant setIcon() ), alors je viens de changer de casting à ActionMenuItemView , le filtre de couleur appliqué à gauche tiré de getCompoundDrawables() et Voila! Ça marche!

    Il y a une meilleure solution. Vous pouvez le faire par programme dans l'exécution

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

    Alto!

    Les gars, je l'ai fait de manière simple, regardez cet extrait comme suit:

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

    S'il vous plaît, faites-moi savoir s'il n'y a plus de précision ici.

    coAndroid est un fan Android de Google, tout sur les téléphones Android, Android Wear, Android Dev et Android Games Apps.