如何在NavigationView中为项目设置自定义字体?

时间:2015-06-05 13:54:18

标签: android android-design-library navigationview

使用新的NavigationView,我们可以通过XML菜单资源设置抽屉的项目。

有了这个,我们可以用

之类的东西设置每个项目
<item
  android:id="@+id/drawer_my_account"
  android:icon="@drawable/ic_my_account"
  android:title="@string/drawer_my_account" />

但是现在,我想为抽屉中的每个项目设置一个自定义字体,我无法通过XML或Java代码找到方法。有办法吗?

15 个答案:

答案 0 :(得分:161)

只需将以下类文件添加到您的项目中即可。

#define DECL_VAR int VAR_NAME

然后为您的活动创建以下方法

VAR_NAME

并从活动中调用它。

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

这是我的输出

enter image description here

答案 1 :(得分:74)

这个为我工作

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

水库&GT;值 - &GT;花柱

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

//设置自定义字体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();
            }
        }


}

答案 2 :(得分:15)

使用 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_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">

在styles.xml中写

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

答案 3 :(得分:14)

  

有办法吗?

是。 NavigationView没有提供直接处理方法,但可以使用View.findViewsWithText轻松实现。

有两件事可以帮助我们解决这个问题。

  1. 每个MenuItem视图都是TextView。因此,这样可以更轻松地应用Typeface。有关TextView实际使用的NavigationView的详细信息,请参阅NavigationMenuItemView
  2. 选择NavigationView时,
  3. MenuItem provides a callback。我们将不得不为每个MenuItem提供一个唯一的ID,这个回调将尽可能地帮助生成这些ID,这意味着稍后会少一些代码。但是,这与您是否拥有SubMenu
  4. 更相关

    <强>实施

    请注意,每个MenuItem ID都只是menuItem+Position。当我们为每个View找到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);
                }
            }
        });
    }
    

    您可以看到如何使用通用MenuItem ID允许您使用Resources.getIdentifier并保存几行代码。

    SubMenu警告

    要记住一些事情。您需要明确循环使用N菜单项,而不是使用Menu.size。否则,您的SubMenu项将无法识别。换句话说,如果你没有SubMenu,另一种方法是:

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

    您不必担心为每个MenuItem应用唯一ID。

    <强>结果

    results

    我在示例中使用的字体是:Smoothie Shoppe

答案 4 :(得分:6)

我使用过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>

答案 5 :(得分:3)

不是自定义字体,而是另一种更改导航项的方法&#39;字体。创建名为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" />

然后将fontFamily更改为所需的字体。

答案 6 :(得分:3)

回答有点迟,但我找到了一种更干净的方法,所以我想分享一下。

  • 制作自定义视图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);
    }
    }
    
  • 制作一个名为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);
        }
    }
    }
    
  • 制作布局layout/design_navigation_item.xml(名称必须完全相同):

    <?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" />
    
  • 将字体文件SourceSansPro-Bold.ttf放在此路径中:app/src/main/assets/fonts/SourceSansPro-Bold.ttf

你很高兴!这样,您可以保持主要活动的清洁。

这是一个截图: enter image description here

答案 7 :(得分:2)

对于那些使用@Moinkhan答案的人来说,将字体应用到菜单的每个部分都使用该解决方案,并且每个标题部分都使用id。 这样的菜单..

a

和这样的解决方案..

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

也许对某人有帮助。

答案 8 :(得分:2)

我真的很喜欢“吐龙”的解决方案#34;但是我没有得到textview。这可以通过执行以下操作来完成:

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>

答案 9 :(得分:2)

我已经重构 @ adneal的答案。它根据索引而不是id遍历菜单项(不进入子项,只有顶级项)并设置字体。

rightNavigationView 替换为您的NavigationView,将 {TYPEFACE} 替换为您想要的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);
                }
            }
        });

答案 10 :(得分:2)

设置自定义字体的另一种方法:

1。您可以使用&#34;字体&#34;添加字体。文件夹,然后您可以在任何TextView(或您需要的任何地方)使用它们

enter image description here

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. 在styles.xml文件中,您可以根据需要随时自定义具有该字体和颜色的项目文字样式(来自@Moonis Abidi答案)

 <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。现在,您只需在导航视图中使用 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"/>

// ------------- 此外,如果您需要使用其他TextViews中的此字体,您可以像

一样使用它
 <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/nunito_bold"/>

答案 11 :(得分:0)

这是另一种方法:

NavigationView 的孩子名为 NavigationMenuItemView 。 NavigationMenuItemView有两个子节点。一个是 AppCompatCheckedTextView

覆盖NavigationView的 onLayout 方法,如下所示,并更改AppCompatCheckedTextView的Typefase

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

答案 12 :(得分:0)

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

答案 13 :(得分:0)

在res / font /文件夹中添加字体文件以将字体捆绑为资源

然后

您可以使用样式资源进行更改。在您的styles.xml中:

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

然后将其作为主题应用于您的视图:

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

答案 14 :(得分:-1)

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