How to set custom typeface to items in NavigationView?

AndroidAndroid Design-LibraryNavigationview

Android Problem Overview


With the new NavigationView, we are able to set a drawer's items through a menu resource in XML.

With that, we can set each item with something like

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

But now, I want to set a custom typeface to each of those items in my drawer, and I could not find a way to do that, either by XML or by Java code. Is there a way to do it?

Android Solutions


Solution 1 - Android

just add following class file to your project.

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

then create following method to your activity

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

and call it from activity.

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

and here is my output

enter image description here

Solution 2 - Android

this one working for me

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

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

//to Set Custom Typeface 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();
	        }
	    }

	    
}

Solution 3 - Android

Use the app:itemTextAppearance="" property. Hope this helps.

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

In styles.xml write

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

Solution 4 - Android

> Is there a way to do it?

Yes. The NavigationView doesn't provide a direct way of handling this, but it can easily be achieved using View.findViewsWithText.

There are two things that will help us handle this.

  1. Each MenuItem view is a TextView. So, that makes applying your Typeface that much easier. For more information on the TextView actually used by NavigationView, see NavigationMenuItemView.
  2. NavigationView provides a callback when a MenuItem is selected. We're going to have to supply each MenuItem with a unique id and this callback will help generify those ids as much as possible, which means a little less code later on. Although, this is more related to whether or not you have a SubMenu.

Implementation

Notice each MenuItem id is simply menuItem+Position. This will come in handy later when we're finding the View for each MenuItem.

http://schemas.android.com/apk/res/android">

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

You can see how using a generic MenuItem id allows you to utilize Resources.getIdentifier and save a few lines of code.

SubMenu caveat

Something to keep in mind. You need to explicitly loop over your N menu items rather than using Menu.size. Otherwise, your SubMenu items won't be recognized. In other words, if you don't have a SubMenu, another way of doing this would be:

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

And you don't have to worry about applying a unique id to each MenuItem.

Results

results

The font I'm using in the example is: Smoothie Shoppe

Solution 5 - Android

I have used 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>

Solution 6 - Android

A different way to set your custom font:

1. You can add your fonts in a "font" folder, and then you can use them in any TextView (or wherever you needed)

enter image description here

An example of 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. In your styles.xml file, you can customize your item text style with that font, and color, wherever you need (from @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. Now, you only have to specify this in your navigation view with 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"/>

// ------------- Also, if you need to use this font from others TextViews, you can use it like

 <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/nunito_bold"/>

Solution 7 - Android

Not a custom typeface, but another way to change the navigation items' font. Create a layout named 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" />

Then change the fontFamily to the desired font.

Solution 8 - Android

It's a bit late to answer but I found a cleaner way to do it, so I would like to share.

  • Make a custom view 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);
      }
      }
    
  • Make a file called 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);
          }
      }
      }
    
  • Make a layout layout/design_navigation_item.xml (the name must be exactly the same):

      <?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" />
    
  • Place your font file SourceSansPro-Bold.ttf in this path: app/src/main/assets/fonts/SourceSansPro-Bold.ttf

You're good to go! This way, you can keep your main activity cleaner.

Here's a screenshot: enter image description here

Solution 9 - Android

I've refactored @adneal's answer to this. It loops over the menu items(without going into sub items, only top level items) according to the index instead of the id and sets the Typeface.

Replace rightNavigationView with your NavigationView and {TYPEFACE} with your desired TypeFace

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

Solution 10 - Android

for those who use @Moinkhan answer, for applying font to every part of your menus use that solution and for every header section use id. your menu like this..

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

and solution like this..

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

maybe it helps someone.

Solution 11 - Android

I really loved the solution of "Dragon who spits fire"'s but I didn't get the textview. This could be done by doing the following:

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

http://schemas.android.com/apk/res/android" style="@style/Body1" android:layout_width="match_parent" android:layout_height="?attr/listPreferredItemHeightSmall" android:focusable="true" android:foreground="?attr/selectableItemBackground" android:paddingEnd="?attr/listPreferredItemPaddingRight" android:paddingLeft="?attr/listPreferredItemPaddingLeft" android:paddingRight="?attr/listPreferredItemPaddingRight" android:paddingStart="?attr/listPreferredItemPaddingLeft" />

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>

Solution 12 - Android

Add the font file in the res/font/ folder to bundle fonts as resources, then You can change it using style resources. In your styles.xml :

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

Then apply it as a theme in your view:

<android.support.design.widget.BottomNavigationView
   ...
   android:theme="@style/Widget.BottomNavigationView"
/>

Solution 13 - Android

This is another approach:

A NavigationView has children called NavigationMenuItemView. A NavigationMenuItemView has two children. One is AppCompatCheckedTextView.

Override onLayout method of the NavigationView like bellow and change Typefase of the 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);
            }
        }
    }
}

Solution 14 - Android

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

Solution 15 - Android

applyFontToMenuItem(popup.getMenu().getItem(0));
private void applyFontToMenuItem(MenuItem mi) {
    Typeface font = Typeface.createFromAsset(getAssets(), "fonts/Redressed.ttf");       
    SpannableString mNewTitle = new SpannableString(mi.getTitle());
    mNewTitle.setSpan(new CustomTypefaceSpan("", font), 0, mNewTitle.length(),pannable.SPAN_INCLUSIVE_INCLUSIVE);
    mi.setTitle(mNewTitle);
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJ&#250;lio ZyngerView Question on Stackoverflow
Solution 1 - AndroidMoinkhanView Answer on Stackoverflow
Solution 2 - AndroidPankaj Kant PatelView Answer on Stackoverflow
Solution 3 - AndroidMoonis AbidiView Answer on Stackoverflow
Solution 4 - AndroidadnealView Answer on Stackoverflow
Solution 5 - AndroidAndrew GlukhoffView Answer on Stackoverflow
Solution 6 - AndroidSolArabehetyView Answer on Stackoverflow
Solution 7 - Androidi906View Answer on Stackoverflow
Solution 8 - AndroidI'm a frog dragonView Answer on Stackoverflow
Solution 9 - AndroidMalek HijaziView Answer on Stackoverflow
Solution 10 - AndroidSetmaxView Answer on Stackoverflow
Solution 11 - Androiduser447811View Answer on Stackoverflow
Solution 12 - AndroidBrinda RathodView Answer on Stackoverflow
Solution 13 - AndroidArashView Answer on Stackoverflow
Solution 14 - AndroidMGuptView Answer on Stackoverflow
Solution 15 - AndroidDivs WeapplinseView Answer on Stackoverflow