How to set custom typeface to items in NavigationV

2019-01-07 03:36发布

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?

14条回答
我欲成王,谁敢阻挡
2楼-- · 2019-01-07 04:15
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);
    }
查看更多
劫难
3楼-- · 2019-01-07 04:16

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"/>
查看更多
叛逆
4楼-- · 2019-01-07 04:20

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.

<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

查看更多
Root(大扎)
5楼-- · 2019-01-07 04:20

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.

查看更多
Juvenile、少年°
6楼-- · 2019-01-07 04:20
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);
}
查看更多
成全新的幸福
7楼-- · 2019-01-07 04:21

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);
            }
        }
    }
}
查看更多
登录 后发表回答