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

Android 如何为NavigationView中的项目设置自定义字体?,android,android-design-library,navigationview,Android,Android Design Library,Navigationview,使用新的导航视图,我们可以通过XML中的菜单资源设置抽屉中的项目 有了它,我们可以将每个项目设置为 <item android:id="@+id/drawer_my_account" android:icon="@drawable/ic_my_account" android:title="@string/drawer_my_account" /> 但现在,我想为抽屉中的每一项设置一个自定义字体,但我找不到一种方法,不管是通过XML还是Java代码。有办法吗 有办法

使用新的
导航视图
,我们可以通过XML中的菜单资源设置抽屉中的项目

有了它,我们可以将每个项目设置为

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

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

有办法吗

对。虽然没有提供一种直接的处理方法,但是可以使用

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

  • 每个
    MenuItem
    视图都是
    TextView
    。因此,这使得应用
    字体
    变得更加容易。有关
    导航视图实际使用的
    文本视图
    的更多信息,请参阅
  • NavigationView
    当选择
    MenuItem
    时。我们必须为每个
    MenuItem
    提供一个唯一的id,这个回调将尽可能多地帮助生成这些id,这意味着以后代码会少一些。尽管如此,这与您是否有
    子菜单有关
  • 实施

    请注意,每个
    MenuItem
    id只是
    MenuItem+Position
    。稍后,当我们为每个
    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>
    

    您不必担心对每个
    菜单项应用唯一的id

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


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

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

    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);
        }
    }
    
    然后为您的活动创建以下方法

    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);
    }
    
    从活动中调用它

    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);
            }
    
    navView=(NavigationView)findviewbyd(R.id.navView);
    Menu m=navView.getMenu();
    对于(int i=0;i0){
    
    对于(int j=0;j不是自定义字体,而是更改导航项字体的另一种方式。创建名为
    design\u navigation\u 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更改为所需字体。

    对于使用@Moinkhan answer的用户,对于将字体应用到菜单的每个部分,请使用该解决方案,对于每个标题部分,请使用id。 你的菜单是这样的

    <item android:title="@string/others" android:id="@+id/nav_others">
        <menu>
            <item
                android:id="@+id/contact"
                android:title="@string/contact"/>
        </menu>
    </item>
    
    navMenu = navView.getMenu();
        MenuItem item= navView.getMenu().findItem(R.id.nav_others);
        applyFontToMenuItem(item);
    

    也许它对某人有帮助。

    这个对我有用

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

    现在回答有点晚,但我找到了一个更干净的方法,所以我想与大家分享

    • 创建自定义视图
      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\u navigation\u item.xml
      (名称必须完全相同):

      
      
    • 将字体文件
      SourceSansPro Bold.ttf
      放在以下路径中:
      app/src/main/assets/fonts/SourceSansPro Bold.ttf

    你可以走了!这样,你可以保持你的主要活动更干净

    以下是一个屏幕截图:

    我非常喜欢“喷火龙”的解决方案,但我没有得到
    文本视图

    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>
    
    
    Quicksand-Regular.otf
    
    attrs.xml:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
    <declare-styleable name="ProjectView">
        <attr name="projectFont" format="string" />
    </declare-styleable>
    </resources>
    

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

    用导航视图替换rightNavigationView,用所需字体替换{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);
                    }
                }
            });
    
    final Menu navMenu=rightNavigationView.getMenu();
    rightNavigationView.getViewTreeObserver().addOnGlobalLayoutListener(新ViewTreeObserver.OnGlobalLayoutListener()){
    @凌驾
    公共图书馆{
    ArrayList menuItems=新建ArrayList();//在此数组中保存视图
    rightNavigationView.getViewTreeObserver()
    for(int i=0;i
    使用应用程序: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>
    
    
    @彩色/彩色白色
    12便士
    无衬线薄
    
    这是另一种方法:

    具有名为NavigationMenuItemView的子项。NavigationMenuItemView具有两个子项。一个子项为

    覆盖导航视图的onLayout方法,如下图和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);
                }
            }
        }
    }
    
    public final class NavigationView with CustomFont扩展了NavigationView{
    私人最终语境;
    私人字体字体;
    带有自定义字体的公共导航视图(上下文、属性集属性){
    超级(上下文,attrs);
    this.context=上下文;
    this.fontFace=null;
    }
    @凌驾
    仅限受保护的空心布局(布尔值已更改、整数左侧、整数顶部、整数右侧、整数底部){
    超级。仅限布局(已更改、左、上、右、下);
    最终视图组navMenuView=(视图组)getChildAt(0);
    final int NavMenuItemScont=navMenuView.getChildCount();
    视图组项目视图;
    如果(fontFace==null){
    fontFace=Typeface.createFromAsset(context.getAssets(),context.getString(R.string.BTrafficBold));
    
    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);
    }
    
    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);
                    }
                }
            });
    
     <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">
    
    <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>
    
    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);
                }
            }
        }
    }
    
    <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"
       />
    
    <style name="NavigationViewTextAppearance">
        <item name="android:ellipsize">end</item>
        <item name="android:fontFamily">@font/badscript_regular</item>
    </style>
    
    <?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>
    
     <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>
    
    <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"/>
    
     <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="@font/nunito_bold"/>
    
    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);
        }
    
    <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"
    />