web-dev-qa-db-fra.com

Comment définir une police de caractères personnalisée pour les éléments dans NavigationView?

Avec la nouvelle NavigationView, nous sommes en mesure de définir les éléments d'un tiroir via une ressource de menu au format XML.

Avec cela, nous pouvons définir chaque élément avec quelque chose comme

<item
  Android:id="@+id/drawer_my_account"
  Android:icon="@drawable/ic_my_account"
  Android:title="@string/drawer_my_account" />

Mais maintenant, je veux définir une police de caractères personnalisée pour chacun de ces éléments dans mon tiroir, et je ne pouvais pas trouver un moyen de le faire, ni par XML ni par du code Java. Y a-t-il un moyen de le faire?

61
Júlio Zynger

ajoutez simplement le fichier de classe suivant à votre projet. 

import Android.graphics.Paint;
import Android.graphics.Typeface;
import Android.text.TextPaint;
import Android.text.style.TypefaceSpan;

public class CustomTypefaceSpan extends TypefaceSpan {

    private final Typeface newType;

    public CustomTypefaceSpan(String family, Typeface type) {
        super(family);
        newType = type;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        applyCustomTypeFace(ds, newType);
    }

    @Override
    public void updateMeasureState(TextPaint Paint) {
        applyCustomTypeFace(Paint, newType);
    }

    private static void applyCustomTypeFace(Paint paint, Typeface tf) {
        int oldStyle;
        Typeface old = Paint.getTypeface();
        if (old == null) {
            oldStyle = 0;
        } else {
            oldStyle = old.getStyle();
        }

        int fake = oldStyle & ~tf.getStyle();
        if ((fake & Typeface.BOLD) != 0) {
            Paint.setFakeBoldText(true);
        }

        if ((fake & Typeface.ITALIC) != 0) {
            Paint.setTextSkewX(-0.25f);
        }

        Paint.setTypeface(tf);
    }
}

puis créez la méthode suivante pour votre activité 

private void applyFontToMenuItem(MenuItem mi) {
        Typeface font = Typeface.createFromAsset(getAssets(), "ds_digi_b.TTF");
        SpannableString mNewTitle = new SpannableString(mi.getTitle());
        mNewTitle.setSpan(new CustomTypefaceSpan("" , font), 0 , mNewTitle.length(),  Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        mi.setTitle(mNewTitle);
}

et appelez-le de l'activité.

navView = (NavigationView) findViewById(R.id.navView);
        Menu m = navView.getMenu();
        for (int i=0;i<m.size();i++) {
            MenuItem mi = m.getItem(i);

            //for aapplying a font to subMenu ...
            SubMenu subMenu = mi.getSubMenu();
            if (subMenu!=null && subMenu.size() >0 ) {
                for (int j=0; j <subMenu.size();j++) {
                    MenuItem subMenuItem = subMenu.getItem(j);
                    applyFontToMenuItem(subMenuItem);
                }
            }

            //the method we have create in activity
            applyFontToMenuItem(mi);
        }

et voici ma sortie

enter image description here

160
Moinkhan

celui-ci travaille pour moi

<Android.support.design.widget.NavigationView
       Android:id="@+id/navigation_view"
       Android:layout_width="wrap_content"
       Android:layout_height="match_parent"
       Android:layout_gravity="start"
       Android:background="#4A4444"
       Android:clipToPadding="false"
       Android:paddingBottom="50dp"
       app:itemIconTint="@color/white"
       app:menu="@menu/drawer_home"
       app1:itemTextAppearance="@style/NavigationDrawerStyle" >
</Android.support.design.widget.NavigationView>

res-> valeurs-> styles

 <style name="NavigationDrawerStyle">
    <item name="Android:textSize">18sp</item>
    <item name="Android:typeface">monospace</item>
</style>

// pour définir une police de caractères personnalisée MainApplication.Java

public class MainApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

//set Custom Typeface

        FontsOverride.setDefaultFont(this, "MONOSPACE", "OpenSans-Semibold.ttf");
    }
}

// FontsOverride.Java

public final class FontsOverride {

     public static void setDefaultFont(Context context,
                String staticTypefaceFieldName, String fontAssetName) {
            final Typeface regular = Typeface.createFromAsset(context.getAssets(),
                    fontAssetName);
            replaceFont(staticTypefaceFieldName, regular);
        }

        protected static void replaceFont(String staticTypefaceFieldName,
                final Typeface newTypeface) {
            try {
                final Field staticField = Typeface.class
                        .getDeclaredField(staticTypefaceFieldName);
                staticField.setAccessible(true);

                staticField.set(null, newTypeface);
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }


}
73
Pankaj Kant Patel

Utilisez leapp: itemTextAppearance = ""property . J'espère que cela vous aidera.

 <Android.support.design.widget.NavigationView
        Android:id="@+id/nav_view"
        Android:layout_width="wrap_content"
        Android:layout_height="match_parent"
        Android:layout_gravity="start"
        Android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main"
        Android:background="@drawable/nav_bg_gradient"
        Android:theme="@style/NavigationView"
        app:itemIconTint="@color/colorWhite"
        app:itemTextColor="@color/colorWhite"
        app:itemTextAppearance="@style/NavigationText"
        app:menu="@menu/main_drawer">

Dans styles.xml, écrivez

<style name="NavigationText" parent="@Android:style/TextAppearance.Medium">
        <item name="Android:textColor">@color/colorWhite</item>
        <item name="Android:textSize">12sp</item>
        <item name="Android:fontFamily">sans-serif-thin</item>
    </style>
14
Moonis Abidi

Y a-t-il un moyen de le faire?

Oui. La NavigationView ne fournit pas un moyen direct de gérer cela, mais elle peut être facilement réalisée avec View.findViewsWithText .

Deux choses nous aideront à gérer cela.

  1. Chaque vue MenuItem est une TextView. Cela facilite donc beaucoup l'application de votre Typeface. Pour plus d'informations sur la TextView réellement utilisée par NavigationView, voir NavigationMenuItemView
  2. NavigationViewfournit un rappel quand une MenuItem est sélectionnée. Nous allons devoir fournir à chaque MenuItem un identifiant unique. Ce rappel aidera à générer ces identifiants autant que possible, ce qui signifie un peu moins de code par la suite. Bien que cela soit davantage lié au fait que vous ayez ou non une SubMenu.

La mise en oeuvre

Notez que chaque ID MenuItem est simplement menuItem+Position. Cela nous sera utile plus tard, lorsque nous aurons trouvé la View pour chaque MenuItem.

<group Android:checkableBehavior="single">
    <item
        Android:id="@+id/menuItem1"
        Android:icon="@drawable/ic_dashboard"
        Android:title="MenuItem 1" />
    <item
        Android:id="@+id/menuItem2"
        Android:icon="@drawable/ic_event"
        Android:title="MenuItem 2" />
    <item
        Android:id="@+id/menuItem3"
        Android:icon="@drawable/ic_headset"
        Android:title="MenuItem 3" />
    <item
        Android:id="@+id/menuItem4"
        Android:icon="@drawable/ic_forum"
        Android:title="MenuItem 4" />
</group>

<item Android:title="Sub items" >
    <menu>
        <item
            Android:id="@+id/menuItem5"
            Android:icon="@drawable/ic_dashboard"
            Android:title="Sub item 5" />
        <item
            Android:id="@+id/menuItem6"
            Android:icon="@drawable/ic_forum"
            Android:title="Sub item 6" />
    </menu>
</item>


/** The total number of menu items in the {@link NavigationView} */
private static final int MENU_ITEMS = 6;
/** Contains the {@link MenuItem} views in the {@link NavigationView} */
private final ArrayList<View> mMenuItems = new ArrayList<>(MENU_ITEMS);

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
    final NavigationView navView = ...
    // Grab the NavigationView Menu
    final Menu navMenu = navView.getMenu();
    // Install an OnGlobalLayoutListener and wait for the NavigationMenu to fully initialize
    navView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            // Remember to remove the installed OnGlobalLayoutListener
            navView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            // Loop through and find each MenuItem View
            for (int i = 0, length = MENU_ITEMS; i < length; i++) {
                final String id = "menuItem" + (i + 1);
                final MenuItem item = navMenu.findItem(getResources().getIdentifier(id, "id", getPackageName()));
                navView.findViewsWithText(mMenuItems, item.getTitle(), View.FIND_VIEWS_WITH_TEXT);
            }
            // Loop through each MenuItem View and apply your custom Typeface
            for (final View menuItem : mMenuItems) {
                ((TextView) menuItem).setTypeface(yourTypeface, Typeface.BOLD);
            }
        }
    });
}

Vous pouvez voir comment l’utilisation d’un identifiant MenuItem générique vous permet d’utiliser Resources.getIdentifier et d’économiser quelques lignes de code. 

SubMenu mise en garde

Quelque chose à garder à l'esprit. Vous devez explicitement boucler sur vos éléments de menu N plutôt que d'utiliser Menu.size . Sinon, vos éléments SubMenu ne seront pas reconnus. En d'autres termes, si vous n'avez pas de SubMenu, une autre façon de procéder serait:

for (int i = 0, length = navMenu.size(); i < length; i++) {
    final MenuItem item = navMenu.getItem(i);
    navigationView.findViewsWithText(mMenuItems, item.getTitle(), View.FIND_VIEWS_WITH_TEXT);
}

Et vous n'avez pas à vous soucier d'appliquer un identifiant unique à chaque MenuItem.

Résultats

results

La police que j'utilise dans l'exemple est la suivante: Smoothie Shoppe

14
adneal

J'ai utilisé app: theme

<Android.support.design.widget.NavigationView
    Android:id="@+id/nav_view"
    Android:layout_width="wrap_content"
    Android:layout_height="match_parent"
    Android:layout_gravity="start"
    Android:background="@color/colorMenuBackground"
    Android:fitsSystemWindows="true"
    app:headerLayout="@layout/nav_header_main"
    app:menu="@menu/activity_main_drawer"
    app:theme="@style/NavigationViewTextAppearance"
   />

Styles.xml:

<style name="NavigationViewTextAppearance">
    <item name="Android:ellipsize">end</item>
    <item name="Android:fontFamily">@font/badscript_regular</item>
</style>
6
Andrew Glukhoff

C'est un peu tard pour répondre mais j'ai trouvé une façon plus propre de le faire, alors j'aimerais partager.

  • Créer une vue personnalisée NavFontTextView.Java

    import Android.content.Context;
    import Android.support.design.internal.NavigationMenuItemView;
    import Android.util.AttributeSet;
    
    import utils.CustomFontHelper;
    
    public class NavFontTextView extends NavigationMenuItemView {
    Context mContext;
    
    public NavFontTextView(Context context) {
        super(context);
        mContext = context;
        setDefaultFont();
    }
    
    public NavFontTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        setDefaultFont();
        CustomFontHelper.setCustomFont(this, context, attrs);
    }
    
    public NavFontTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        setDefaultFont();
        CustomFontHelper.setCustomFont(this, context, attrs);
    }
    
    public void setDefaultFont() {
        CustomFontHelper.setCustomFont(this, "fonts/SourceSansPro-Regular.ttf", mContext);
    }
    }
    
  • Créez un fichier appelé CustomFontHelper.Java:

    import Android.content.Context;
    import Android.content.res.TypedArray;
    import Android.graphics.Typeface;
    import Android.util.AttributeSet;
    import Android.widget.TextView;
    
    /**
     * Taken from: http://stackoverflow.com/a/16648457/75579
     */
    public class CustomFontHelper {
    /**
     * Sets a font on a textview based on the custom com.my.package:font attribute
     * If the custom font attribute isn't found in the attributes nothing happens
     * @param textview
     * @param context
     * @param attrs
     */
    public static void setCustomFont(TextView textview, Context context, AttributeSet attrs) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomFont);
        String font = a.getString(R.styleable.CustomFont_font);
        setCustomFont(textview, font, context);
        a.recycle();
    }
    
    /**
     * Sets a font on a textview
     * @param textview
     * @param font
     * @param context
     */
    public static void setCustomFont(TextView textview, String font, Context context) {
        if(font == null) {
            return;
        }
        Typeface tf = FontCache.get(font, context);
        if(tf != null) {
            textview.setTypeface(tf);
        }
    }
    }
    
  • Créez un layout layout/design_navigation_item.xml (le nom doit être exactement le même):

    <?xml version="1.0" encoding="utf-8"?>
    <custom_view.NavFontTextView xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:app="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="match_parent"
    Android:layout_height="?attr/listPreferredItemHeightSmall"
    Android:drawablePadding="10dp"
    Android:gravity="center_vertical|start"
    Android:maxLines="1"
    Android:paddingLeft="?attr/listPreferredItemPaddingLeft"
    Android:paddingRight="?attr/listPreferredItemPaddingRight"
    app:font="fonts/SourceSansPro-Bold.ttf" />
    
  • Placez votre fichier de police SourceSansPro-Bold.ttf dans ce chemin: app/src/main/assets/fonts/SourceSansPro-Bold.ttf

Vous êtes prêt à partir! De cette façon, vous pouvez garder votre activité principale plus propre.

Voici une capture d'écran:  enter image description here

3
I'm a frog dragon

Pas une police de caractères personnalisée, mais un autre moyen de changer la police des éléments de navigation. Créez une mise en page nommée design_navigation_item.xml.

<Android.support.design.internal.NavigationMenuItemView
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="?attr/listPreferredItemHeightSmall"
    Android:paddingLeft="?attr/listPreferredItemPaddingLeft"
    Android:paddingRight="?attr/listPreferredItemPaddingRight"
    Android:drawablePadding="@dimen/navigation_icon_padding"
    Android:gravity="center_vertical|start"
    Android:maxLines="1"
    Android:fontFamily="sans-serif-thin"
    Android:textSize="22sp"
    Android:textAppearance="?attr/textAppearanceListItem" />

Puis changez la police fontFamily en la police souhaitée.

3
i906

pour ceux qui utilisent @Moinkhan answer, pour appliquer la police à chaque partie de vos menus, utilisez cette solution et pour chaque section d'en-tête, utilisez id .

<item Android:title="@string/others" Android:id="@+id/nav_others">
    <menu>
        <item
            Android:id="@+id/contact"
            Android:title="@string/contact"/>
    </menu>
</item>

et solution comme ça ..

navMenu = navView.getMenu();
    MenuItem item= navView.getMenu().findItem(R.id.nav_others);
    applyFontToMenuItem(item);

peut-être que ça aide quelqu'un.

2
Setmax

J'ai vraiment adoré la solution de "Dragon qui crache du feu" mais je n'ai pas eu le textview. Cela pourrait être fait en procédant comme suit:

TextView textView = (CheckedTextView) findViewById(Android.support.design.R.id.design_menu_item_text);

public class StyledMenuItem extends NavigationMenuItemView {
public StyledMenuItem(Context context) {
    super(context);
}

public StyledMenuItem(Context context, AttributeSet attrs) {
    super(context, attrs);
    if (!isInEditMode()) {
        setCustomFont(context, attrs);
        setFilterTouchesWhenObscured(true);
    }
}

public StyledMenuItem(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    if (!isInEditMode()) {
        setCustomFont(context, attrs);
        setFilterTouchesWhenObscured(true);
    }
}

private void setCustomFont(Context ctx, AttributeSet attrs) {
    TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.ProjectView);
    String customFont = a.getString(R.styleable.ProjectView_projectFont);
    setCustomFont(ctx, customFont);
    a.recycle();
}

private void setCustomFont(Context ctx, String asset) {
    Typeface typeFace = TypeFaceProvider.getTypeFace(ctx, asset);
    TextView textView = (CheckedTextView) findViewById(Android.support.design.R.id.design_menu_item_text);
    if (typeFace != null && textView != null) {
        textView.setTypeface(typeFace);
    }
}

design_navigation_item.xml:

<?xml version="1.0" encoding="utf-8"?>

style.xml:

<style name="Body1" parent="Base.TextAppearance.AppCompat.Body1">
    <item name="projectFont">Quicksand-Regular.otf</item>
</style>

attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>

<declare-styleable name="ProjectView">
    <attr name="projectFont" format="string" />
</declare-styleable>
</resources>
2
user447811

J'ai refactored @ adneal's répondre à cela. Il boucle sur les éléments de menu (sans entrer dans les sous-éléments, mais uniquement les éléments de niveau supérieur) en fonction de l'index au lieu de l'id et définit le type de caractère.

RemplacezrightNavigationViewpar votre NavigationView et{TYPEFACE}par votre TypeFace souhaité.

final Menu navMenu = rightNavigationView.getMenu();
        rightNavigationView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                ArrayList<View> menuItems = new ArrayList<>(); // save Views in this array
                rightNavigationView.getViewTreeObserver().removeOnGlobalLayoutListener(this); // remove the global layout listener
                for (int i = 0; i < navMenu.size(); i++) {// loops over menu items  to get the text view from each menu item
                    final MenuItem item = navMenu.getItem(i);
                    rightNavigationView.findViewsWithText(menuItems, item.getTitle(), View.FIND_VIEWS_WITH_TEXT);
                }
                for (final View menuItem : menuItems) {// loops over the saved views and sets the font
                    ((TextView) menuItem).setTypeface({TYPE}, Typeface.BOLD);
                }
            }
        });
2
Malek Hijazi

Une autre façon de définir votre police personnalisée:

1. Vous pouvez ajouter vos polices dans un dossier "police", puis les utiliser dans n’importe quel TextView (ou là où vous en avez besoin).

enter image description here

Un exemple de font.xml: 

<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <font
        Android:font="@font/nunito_bold"
        Android:fontStyle="normal"
        Android:fontWeight="400" />
</font-family>

2. Dans votre fichier styles.xml, vous pouvez personnaliser le style de texte de votre article avec cette police et cette couleur, où vous le souhaitez (à partir de @Moonis Abidi answer)

 <style name="NavigationText" parent="@Android:style/TextAppearance.Medium">
        <item name="Android:textColor">@Android:color/white</item>
        <item name="Android:textSize">12sp</item>
        <item name="Android:fontFamily">@font/nunito_semibold</item>
    </style>

3. Maintenant, il vous suffit de spécifier ceci dans votre vue de navigation avec app: itemTextAppearance:

<Android.support.design.widget.NavigationView
        Android:id="@+id/nav_view"
        Android:layout_width="wrap_content"
        Android:layout_height="match_parent"
        Android:layout_gravity="start"
        Android:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/main_menu"
        app:itemTextAppearance="@style/NavigationText"/>

// ------------- En outre, si vous devez utiliser cette police à partir d'autres TextViews, vous pouvez l'utiliser comme

 <TextView
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:fontFamily="@font/nunito_bold"/>
2
SolArabehety
BottomNavigationView bottom_nav = findViewById(R.id.bottom_nav);
Typeface font = Typeface.createFromAsset(getAssets(), "--your customized font file--");
for (int i = 0; i <bottom_nav.getMenu().size(); i++) {
        MenuItem menuItem = bottom_nav.getMenu().getItem(i);
        SpannableStringBuilder spannableTitle = new SpannableStringBuilder(menuItem.getTitle());
        spannableTitle.setSpan(font.getStyle(), 0, spannableTitle.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
        menuItem.setTitle(spannableTitle);
    }
0
MGupt

C'est une autre approche:

Un NavigationView a des enfants appelés NavigationMenuItemView. Un NavigationMenuItemView a deux enfants. On est AppCompatCheckedTextView.

Remplacer la méthode onLayout de NavigationView comme ci-dessous et changer Typefase de AppCompatCheckedTextView:

public final class NavigationViewWithCustomFont extends NavigationView{
    private final Context context;
    private Typeface fontFace;

    public NavigationViewWithCustomFont(Context context, AttributeSet attrs){
        super(context, attrs);
        this.context = context;
        this.fontFace = null;
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom){
        super.onLayout(changed, left, top, right, bottom);
        final ViewGroup navMenuView = (ViewGroup)getChildAt(0);
        final int navMenuItemsCount = navMenuView.getChildCount();
        ViewGroup itemView;

        if(fontFace == null){
            fontFace = Typeface.createFromAsset(context.getAssets(), context.getString(R.string.BTrafficBold));
        }
        for(int i=0; i<navMenuItemsCount; i++){
            itemView = (ViewGroup)navMenuView.getChildAt(i);

            if(itemView instanceof NavigationMenuItemView ){
                CheckedTextView checkedTextView = (CheckedTextView)itemView.getChildAt(0);
                checkedTextView.setTypeface(fontFace, Typeface.BOLD);
            }
        }
    }
}
0
Arash

ajoutez le fichier de police dans le dossier res/font/pour regrouper les polices en tant que ressources

puis

Vous pouvez le changer en utilisant des ressources de style. Dans votre styles.xml:

<style name="Widget.BottomNavigationView" 
   parent="Widget.Design.BottomNavigationView">
   <item name="fontFamily">@font/your_font</item>
</style>

Puis appliquez-le comme thème dans votre vue:

<Android.support.design.widget.BottomNavigationView
...
Android:theme="@style/Widget.BottomNavigationView"
/>
0
Brinda Rathod