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

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

通过这个,我们可以为每个项目设置类似于

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

但是现在,我想为我抽屉里的每个项目都设置一个自定义字体,但是我找不到一种方法来做到这一点,无论是通过 XML 还是 Java 代码。有办法吗?

61019 次浏览

有办法吗?

是的。 NavigationView没有提供一个直接的方式来处理这个问题,但是它可以很容易地使用 View.findViewsWithText来实现。

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

  1. 每个 MenuItem视图都是一个 TextView。因此,这使得应用 Typeface变得更加容易。有关 NavigationView实际使用的 TextView的更多信息,请参见 NavigationMenuItemView
  2. 当选择 MenuItem时。我们必须为每个 MenuItem提供一个惟一的 id,这个回调将帮助尽可能多地泛化这些 id,这意味着以后代码会少一些。不过,这更多的是与你是否有一个 SubMenu有关。

实施

注意,每个 MenuItem id 都是简单的 menuItem+Position。当我们找到每个 MenuItemView时,这将派上用场。

<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

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

只需将下面的类文件添加到您的项目。

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

这是我的输出

enter image description here

不是自定义字体,而是改变导航项字体的另一种方法。

<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 回答的人,对于应用字体到菜单的每个部分,使用该解决方案,对于每个标题部分,使用 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>

Res-> value-> style

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




}

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

  • 制作自定义视图 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

我真的很喜欢“龙吐火”的解决方案,但我没有得到的 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"?>

Xml:

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

Xml:

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


<declare-styleable name="ProjectView">
<attr name="projectFont" format="string" />
</declare-styleable>
</resources>
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);
}

我已经重构了 @ adNeal’s的答案。它根据索引而不是 id 在菜单项上循环(不进入子项,只进入顶级项) ,并设置 Typeface。

右边导航视图替换为 NavigationView,将 < em > { 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);
}
}
});

使用 App: itemText卖相 = “”属性。 希望这个能帮上忙。

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

这是另一种方法:

一个 href = “ https://developer.android.com/reference/android/support/design/widget/NavigationView.html”rel = “ nofollow noReferrer”> NavigationView 有称为 导航 MenuItemView的子节点。 NavigationMenuItemView 有两个子节点。一个是 一个 href = “ https://developer.android.com/reference/android/support/v7/widget/AppCompatCheckedTextView.html”rel = “ nofollow noReferrer”> AppCompatCheckedTextView

像下面这样覆盖 NavigationView 的 在布局上方法并更改 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);
}
}
}
}

我用过 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"
/>

返回文章页面

<style name="NavigationViewTextAppearance">
<item name="android:ellipsize">end</item>
<item name="android:fontFamily">@font/badscript_regular</item>
</style>

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

1.你可以在“ font”文件夹中添加你的字体,然后你可以在任何 TextView (或者任何你需要的地方)中使用它们

enter image description here

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 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. 现在,你只需要用 App: itemText卖相在你的导航视图中指定:

<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 中使用此字体,您可以像下面这样使用它

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

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