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.

  • OnConnectionFailed donnant SIGN_IN_REQUIRED (4)
  • Comment ajouter automatiquement des milliers de séparateurs lorsque le numéro est entré dans EditText
  • La hauteur de vue Android pour enfants ne correspond pas à un parent dans l'élément ListView
  • NullPointerException lors de la présentation d'un ViewPager une deuxième fois
  • Existe-t-il un moyen de désactiver l'accélération matérielle uniquement pour Android 4.0.3?
  • Layout Layers? Z-Axis?
  • 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.

  • Android / gradle: incluez le nom de la version de la saveur dans le nom de fichier apk
  • OnItemClickListener utilisant ArrayAdapter pour ListView
  • Comment fermer la disposition du tiroir sur BackPress dans Android?
  • Comment puis-je vérifier depuis Android WebView si une page est une "page 404 introuvable"?
  • Ouvrez l'activité en tant que pop-up et pas dans un autre écran dans Android?
  • Comment prendre en charge MOV (quick time) dans Android?
  • 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.