web-dev-qa-db-fra.com

Ajouter un compteur de badges à l'icône du menu de navigation du hamburger dans Android

Ma question est la même que cette question (qui est pas un doublon de cette question ) .

seule réponse à cette question ne fonctionne pas pour moi, plutôt que de changer l'icône de hamburger par défaut en à gauche du titre de l'activité, il ajoute simplement une icône de hamburger supplémentaire au à droite de le titre de mon activité.

Alors, comment puis-je réellement obtenir ceci:

Android hamburger icon with badge counter

Je l'ai fouillé toute la journée, mais je n'ai nulle part.

Je vois que Toolbar a une méthode setNavigationIcon(Drawable drawable). Idéalement, je voudrais utiliser un layout (qui contient l'icône du hamburger et la vue du badge) au lieu d'un Drawable, mais je ne sais pas si/comment cela est réalisable - ou si Il y a un meilleur moyen?

NB - Il ne s'agit pas de savoir comment créer la vue badge. J'ai déjà créé cela et l'ai implémenté sur les éléments du menu nav eux-mêmes. J'ai donc juste besoin d'ajouter une vue de badge similaire à l'icône de hamburger par défaut.

17

Depuis la version 24.2.0 de la bibliothèque de support, la version v7 de ActionBarDrawerToggle propose la méthode setDrawerArrowDrawable() comme moyen de personnaliser l'icône de basculement. DrawerArrowDrawable est la classe qui fournit cette icône par défaut, et elle peut être sous-classée pour la modifier selon les besoins.

Par exemple, la classe BadgeDrawerArrowDrawable remplace la méthode draw() pour ajouter un badge rouge et blanc de base après que la superclasse se soit dessinée. Cela permet de conserver l'animation de la flèche hamburger en dessous.

import Android.content.Context;
import Android.graphics.Color;
import Android.graphics.Canvas;
import Android.graphics.Paint;
import Android.graphics.Rect;
import Android.graphics.Typeface;
import Android.support.v7.graphics.drawable.DrawerArrowDrawable;
import Java.util.Objects;

public class BadgeDrawerArrowDrawable extends DrawerArrowDrawable {

    // Fraction of the drawable's intrinsic size we want the badge to be.
    private static final float SIZE_FACTOR = .3f;
    private static final float HALF_SIZE_FACTOR = SIZE_FACTOR / 2;

    private Paint backgroundPaint;
    private Paint textPaint;
    private String text;
    private boolean enabled = true;

    public BadgeDrawerArrowDrawable(Context context) {
        super(context);

        backgroundPaint = new Paint();
        backgroundPaint.setColor(Color.RED);
        backgroundPaint.setAntiAlias(true);

        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setAntiAlias(true);
        textPaint.setTypeface(Typeface.DEFAULT_BOLD);
        textPaint.setTextAlign(Paint.Align.CENTER);
        textPaint.setTextSize(SIZE_FACTOR * getIntrinsicHeight());
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);

        if (!enabled) {
            return;
        }

        final Rect bounds = getBounds();
        final float x = (1 - HALF_SIZE_FACTOR) * bounds.width();
        final float y = HALF_SIZE_FACTOR * bounds.height();
        canvas.drawCircle(x, y, SIZE_FACTOR * bounds.width(), backgroundPaint);

        if (text == null || text.length() == 0) {
            return;
        }

        final Rect textBounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), textBounds);
        canvas.drawText(text, x, y + textBounds.height() / 2, textPaint);
    }

    public void setEnabled(boolean enabled) {
        if (this.enabled != enabled) {
            this.enabled = enabled;
            invalidateSelf();
        }
    }

    public boolean isEnabled() {
        return enabled;
    }

    public void setText(String text) {
        if (!Objects.equals(this.text, text)) {
            this.text = text;
            invalidateSelf();
        }
    }

    public String getText() {
        return text;
    }

    public void setBackgroundColor(int color) {
        if (backgroundPaint.getColor() != color) {
            backgroundPaint.setColor(color);
            invalidateSelf();
        }
    }

    public int getBackgroundColor() {
        return backgroundPaint.getColor();
    }

    public void setTextColor(int color) {
        if (textPaint.getColor() != color) {
            textPaint.setColor(color);
            invalidateSelf();
        }
    }

    public int getTextColor() {
        return textPaint.getColor();
    }
}

Une instance de ceci peut être définie sur la bascule à tout moment après son instanciation, et les propriétés du badge définies directement sur le dessinable selon les besoins.

Comme l'OP l'a noté ci-dessous, le Context utilisé pour le DrawerArrowDrawable personnalisé doit être obtenu avec ActionBar#getThemedContext() ou Toolbar#getContext() pour garantir que les valeurs de style correctes sont utilisées . Par exemple:

private ActionBarDrawerToggle toggle;
private BadgeDrawerArrowDrawable badgeDrawable;
...

toggle = new ActionBarDrawerToggle(this, ...);
badgeDrawable = new BadgeDrawerArrowDrawable(getSupportActionBar().getThemedContext());

toggle.setDrawerArrowDrawable(badgeDrawable);
badgeDrawable.setText("1");
...

screenshots


Pour simplifier un peu les choses, il peut être préférable de sous-classer également ActionBarDrawerToggle et de tout gérer via l'instance de bascule.

import Android.app.Activity;
import Android.content.Context;
import Android.support.v4.widget.DrawerLayout;
import Android.support.v7.app.ActionBarDrawerToggle;
import Android.support.v7.widget.Toolbar;
import Java.lang.reflect.Field;
import Java.lang.reflect.Method;

public class BadgeDrawerToggle extends ActionBarDrawerToggle {

    private BadgeDrawerArrowDrawable badgeDrawable;

    public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
                             int openDrawerContentDescRes,
                             int closeDrawerContentDescRes) {
        super(activity, drawerLayout, openDrawerContentDescRes,
              closeDrawerContentDescRes);
        init(activity);
    }

    public BadgeDrawerToggle(Activity activity, DrawerLayout drawerLayout,
                             Toolbar toolbar, int openDrawerContentDescRes,
                             int closeDrawerContentDescRes) {
        super(activity, drawerLayout, toolbar, openDrawerContentDescRes,
              closeDrawerContentDescRes);
        init(activity);
    }

    private void init(Activity activity) {
        Context c = getThemedContext();
        if (c == null) {
            c = activity;
        }
        badgeDrawable = new BadgeDrawerArrowDrawable(c);
        setDrawerArrowDrawable(badgeDrawable);
    }

    public void setBadgeEnabled(boolean enabled) {
        badgeDrawable.setEnabled(enabled);
    }

    public boolean isBadgeEnabled() {
        return badgeDrawable.isEnabled();
    }

    public void setBadgeText(String text) {
        badgeDrawable.setText(text);
    }

    public String getBadgeText() {
        return badgeDrawable.getText();
    }

    public void setBadgeColor(int color) {
        badgeDrawable.setBackgroundColor(color);
    }

    public int getBadgeColor() {
        return badgeDrawable.getBackgroundColor();
    }

    public void setBadgeTextColor(int color) {
        badgeDrawable.setTextColor(color);
    }

    public int getBadgeTextColor() {
        return badgeDrawable.getTextColor();
    }

    private Context getThemedContext() {
        // Don't freak about the reflection. ActionBarDrawerToggle
        // itself is already using reflection internally.
        try {
            Field mActivityImplField = ActionBarDrawerToggle.class
                .getDeclaredField("mActivityImpl");
            mActivityImplField.setAccessible(true);
            Object mActivityImpl = mActivityImplField.get(this);
            Method getActionBarThemedContextMethod = mActivityImpl.getClass()
                .getDeclaredMethod("getActionBarThemedContext");
            return (Context) getActionBarThemedContextMethod.invoke(mActivityImpl);
        }
        catch (Exception e) {
            return null;
        }
    }
}

Avec cela, le badge personnalisable à dessiner sera défini automatiquement, et tout ce qui est lié à la bascule peut être géré via un seul objet.

BadgeDrawerToggle est un remplacement direct de ActionBarDrawerToggle, et ses constructeurs sont exactement les mêmes.

private BadgeDrawerToggle badgeToggle;
...

badgeToggle = new BadgeDrawerToggle(this, ...);
badgeToggle.setBadgeText("1");
...
64
Mike M.