web-dev-qa-db-fra.com

Modifier la couleur du texte MenuItem par programmation

J'ai donc un élément de menu, défini comme suit:

<item
    Android:id="@+id/action_live"
    Android:title="@string/action_live"
    Android:orderInCategory="1"
    app:showAsAction="ifRoom|withText" />

Il apparaît sous forme de texte, comme vous pouvez le voir ci-dessous:

 Screenshot 1

Et je veux changer par programme la couleur du texte "LIVE". J'ai cherché pendant un moment et j'ai trouvé une méthode:

Avec défini globalement:

private Menu mOptionsMenu;

et:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    mOptionsMenu = menu;
    getMenuInflater().inflate(R.menu.menu_main, menu);
    return true;
}

Je fais:

MenuItem liveitem = mOptionsMenu.findItem(R.id.action_live);
SpannableString s = new SpannableString(liveitem.getTitle().toString());
s.setSpan(new ForegroundColorSpan(Color.RED), 0, s.length(), 0);
liveitem.setTitle(s);

Mais rien ne se passe!

Si je fais la même chose pour un élément du menu de débordement, cela fonctionne:

 Screenshot 2

Existe-t-il une limitation pour les éléments app: showAsAction = "ifRoom | withText"? Y a-t-il une solution de contournement?

Merci d'avance.

13
danielnovais92

Un peu en retard pour la soirée avec celui-ci, mais j'ai passé un certain temps à travailler dessus et à trouver une solution qui pourrait être utile à quiconque essaierait de faire la même chose. Un crédit revient à Harish Sridharan pour m'avoir conduit dans la bonne direction.

Vous pouvez utiliser findViewById(R.id.MY_MENU_ITEM_ID) pour localiser l'élément de menu (à condition que le menu ait été créé et préparé) et le transformer en une instance TextView comme suggéré par Harish, qui peut ensuite être stylée comme il convient.

public class MyAwesomeActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
    super.onCreate(savedInstanceState);

    // Force invalidatation of the menu to cause onPrepareOptionMenu to be called
    invalidateOptionsMenu();
}

private void styleMenuButton() {
    // Find the menu item you want to style
    View view = findViewById(R.id.YOUR_MENU_ITEM_ID_HERE);

    // Cast to a TextView instance if the menu item was found
    if (view != null && view instanceof TextView) {
        ((TextView) view).setTextColor( Color.BLUE ); // Make text colour blue
        ((TextView) view).setTextSize(TypedValue.COMPLEX_UNIT_SP, 24); // Increase font size
    }
}

@Override
public boolean onPrepareOptionsMenu(Menu menu) {
    boolean result = super.onPrepareOptionsMenu(menu);
    styleMenuButton();
    return result;
}

}

L'astuce consiste ici à forcer l'invalidation du menu dans l'événement onCreate de l'activité (ce qui entraîne l'appel de onPrepareMenuOptions plus tôt que d'habitude). Dans cette méthode, nous pouvons localiser l'élément de menu et le style requis.

17
RRP

@RRP me donne un indice, mais sa solution ne fonctionne pas pour moi. Et @Box en donne une autre, mais sa réponse est un peu moins nette. Merci à eux. Donc, selon eux, j'ai une solution totale.

private static void setMenuTextColor(final Context context, final Toolbar toolbar, final int menuResId, final int colorRes) {
    toolbar.post(new Runnable() {
        @Override
        public void run() {
            View settingsMenuItem =  toolbar.findViewById(menuResId);
            if (settingsMenuItem instanceof TextView) {
                if (DEBUG) {
                    Log.i(TAG, "setMenuTextColor textview");
                }
                TextView tv = (TextView) settingsMenuItem;
                tv.setTextColor(ContextCompat.getColor(context, colorRes));
            } else { // you can ignore this branch, because usually there is not the situation
                Menu menu = toolbar.getMenu();
                MenuItem item = menu.findItem(menuResId);
                SpannableString s = new SpannableString(item.getTitle());
                s.setSpan(new ForegroundColorSpan(ContextCompat.getColor(context, colorRes)), 0, s.length(), 0);
                item.setTitle(s);
            }

        }
    });
}
7
Victor Choy

Il ne devient une vue de texte qu'après inspection. Sa véritable classe est ActionMenuItemView, sur laquelle nous pouvons définir la couleur du texte comme suit:

public static void setToolbarMenuItemTextColor(final Toolbar toolbar,
                                               final @ColorRes int color,
                                               @IdRes final int resId) {
    if (toolbar != null) {
        for (int i = 0; i < toolbar.getChildCount(); i++) {
            final View view = toolbar.getChildAt(i);
            if (view instanceof ActionMenuView) {
                final ActionMenuView actionMenuView = (ActionMenuView) view;
                // view children are accessible only after layout-ing
                actionMenuView.post(new Runnable() {
                    @Override
                    public void run() {
                        for (int j = 0; j < actionMenuView.getChildCount(); j++) {
                            final View innerView = actionMenuView.getChildAt(j);
                            if (innerView instanceof ActionMenuItemView) {
                                final ActionMenuItemView itemView = (ActionMenuItemView) innerView;
                                if (resId == itemView.getId()) {
                                    itemView.setTextColor(ContextCompat.getColor(toolbar.getContext(), color));
                                }
                            }
                        }
                    }
                });
            }
        }
    }
}
5
box

MenuItem tel que défini par la documentation est une interface. Il sera certainement implémenté avec un widget de vue avant d'être présenté sous forme de menu. Dans la plupart des cas, ces éléments de menu sont implémentés en tant que TextView. Vous pouvez utiliser UiAutomatorViewer pour afficher la hiérarchie des vues ou même utiliser hierarchyviewer, qui se trouve dans [sdk-home]/tools /. Ci-joint un exemple d'écran uiautomatorviewer pour un MenuItem

Ainsi, vous pouvez toujours dactylographier votre MenuItem et définir la couleur.

TextView liveitem = (TextView)mOptionsMenu.findItem(R.id.action_live);
liveitem.setTextColor(Color.RED);

MODIFIER:

Comme il y avait une demande pour voir comment utiliser cet outil, j'ajoute un peu plus de contenu.

Assurez-vous d'avoir défini la variable d'environnement $ Android_HOME pointant vers votre SDK HOME.

Dans votre terminal:

cd $Android_HOME
./tools/uiautomatorviewer

Cet outil va s'ouvrir. 

Le deuxième ou le troisième bouton (voir la capture d'écran) du menu permet de capturer la capture d'écran de votre périphérique ou émulateur connecté et vous pouvez inspecter la vue et sa hiérarchie. En cliquant sur la vue, vous décrivez la vue et ses informations. C'est un outil spécialement conçu pour les tests et vous pouvez inspecter n'importe quelle application.

Reportez-vous au site du développeur pour plus d’informations: uiautomatorviewer

 uiautomatorviewer

0
Harish Sridharan

J'ai passé beaucoup de temps là-dessus et je me suis finalement mis au travail. Il existe une solution facile pour Android 6 et 7, mais cela ne fonctionne pas sur Android 5. Ce code fonctionne sur tous. Donc, si vous le faites à Kotlin, voici ma suggestion:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.my_menu, menu)
        setToolbarActionTextColor(menu, R.color.black)
        this.menu = menu
        return true
}

private fun setToolbarActionTextColor(menu: Menu, color: Int) {
        val tb = findViewById<Toolbar>(R.id.toolbar)
        tb?.let { toolbar ->
            toolbar.post {
                val view = findViewById<View>(R.id.my_tag)
                if (view is TextView) {
                    view.setTextColor(ContextCompat.getColor(this, color))
                } else {
                    val mi = menu.findItem(R.id.my_tag)
                    mi?.let {
                        val newTitle: Spannable = SpannableString(it.title.toString())
                        val newColor = ContextCompat.getColor(this, color)
                        newTitle.setSpan(ForegroundColorSpan(newColor),
                                0, newTitle.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
                        it.title = newTitle
                    }
                }
            }
        }
}
0
Hesam