如何为 NavigationView 中的项目设置自定义字体?
Posted
技术标签:
【中文标题】如何为 NavigationView 中的项目设置自定义字体?【英文标题】:How to set custom typeface to items in NavigationView? 【发布时间】:2015-08-20 12:16:12 【问题描述】:使用新的NavigationView
,我们可以通过 XML 中的菜单资源设置抽屉的项目。
这样,我们可以为每个项目设置类似的东西
<item
android:id="@+id/drawer_my_account"
android:icon="@drawable/ic_my_account"
android:title="@string/drawer_my_account" />
但是现在,我想为抽屉中的每一项设置自定义字体,但我找不到通过 XML 或 Java 代码执行此操作的方法。有办法吗?
【问题讨论】:
***.com/a/53379256/787399 【参考方案1】:只需将以下类文件添加到您的项目中。
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);
这是我的输出
【讨论】:
在我的回答中没有 onGobalLayout ..因为我使用自定义类型面跨度 ..因为 SpannableString 允许格式化..但不允许更改我使用自定义类型面的字体跨度类.. 另一种选择是this guy。它解决了自定义字体的问题并添加了字体缓存——如果您要大量使用字体,这是非常重要的。 getAssets() 方法定义在哪里?上面的代码中似乎缺少这一点。 投反对票的人。请尝试给出原因,以便我能理解我的错误。不仅在我的答案上,在任何答案上,如果您不投票,请给出原因。 @AdeelTurk 是的,它给了我那个错误,但我刚刚删除了那个检查。代码就像一个魅力 +1ed。【参考方案2】:这个为我工作
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_
android:layout_
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->样式
<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();
【讨论】:
这是一个非常干净的解决方案 如果和问题有关系就完美了。我们谈论的是字体,而不是字体大小。看看其他答案真的... 嗨 iBobb 我已经更新了答案,因为我知道如何设置自定义字体。谢谢 完美答案。简直太棒了 明亮干净。【参考方案3】:使用 app:itemTextAppearance="" 属性。 希望这会有所帮助。
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_
android:layout_
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>
【讨论】:
@chris838,如果您按照此添加自定义字体,它的工作:developer.android.com/guide/topics/ui/look-and-feel/… 这应该被接受,因为它是最新的解决方案! 完美解决方案。但是,如果您使用自定义字体,请使用<item name="android:fontFamily">@font/custom-font</item>
【参考方案4】:
有办法吗?
是的。 NavigationView
没有提供直接的处理方式,但可以使用 View.findViewsWithText
轻松实现。
有两件事可以帮助我们处理这个问题。
-
每个
MenuItem
视图都是一个TextView
。因此,这使得应用您的Typeface
变得更加容易。有关NavigationView
实际使用的TextView
的更多信息,请参阅NavigationMenuItemView
。
@ 987654337 provides a callback MenuItem
被选中。我们将不得不为每个MenuItem
提供一个唯一的 id,这个回调将帮助尽可能多地生成这些 id,这意味着以后的代码会少一些。不过,这与您是否拥有SubMenu
更相关。
实施
注意每个MenuItem
id 都只是menuItem+Position
。当我们为每个 MenuItem
查找 View
时,这将派上用场。
<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。
结果
我在示例中使用的字体是:Smoothie Shoppe
【讨论】:
首先感谢您的回答。 - - - 我看到您的代码可以设置自定义字体,但我不喜欢使用OnGlobalLayoutListener
并以这种方式遍历 ViewTree
,特别是使用 getResources().getIdentifier(...)
:这正是我要求的原因一个easy way
。此外,这看起来像您依赖项目标题来获取对它们的引用 - 这将使我成为我的头衔的囚徒,对吗?我希望看到一个耦合度较低的选项。
你不会得到一个。 NavigationView
根本没有提供一种“简单”的方式来处理这个问题。我不会担心使用OnGlobalLayoutListener
或使用Resources.getIdentifier
。这些东西可以安全使用,并且也被 Android 框架内部使用。你会成为你头衔的囚徒,是的。我也希望看到一个耦合度较低的选项。我喜欢想象设计库稍后会在这方面更加可定制。
findViewsWithText
无法找到屏幕边界之外的视图。这是预期的行为吗?
正如@user3161880 所说,如果您的菜单碰巧离开屏幕,这种技术将无法设置这些菜单项的样式。我的解决方法是等到使用if(mMenuItems.size() != menu.size()) return;
找到所有项目,然后再删除侦听器并对它们进行样式设置。这可能是性能问题,但在我的特定情况下很好(只需要额外调用一次onGlobalLayout()
)。
当我尝试将 MenuItem 转换为 Textview 时,它变为空。知道这是为什么吗?在尝试进行类型转换之前,我可以毫无问题地更改它的文本。【参考方案5】:
我用过app:theme
<android.support.design.widget.NavigationView
android:id="@+id/nav_view"
android:layout_
android:layout_
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>
【讨论】:
【参考方案6】:另一种设置自定义字体的方法:
1.您可以将字体添加到“字体”文件夹中,然后您可以在任何 TextView(或任何您需要的地方)中使用它们
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_
android:layout_
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_
android:layout_
android:fontFamily="@font/nunito_bold"/>
【讨论】:
这是在android中设置字体的最佳方式【参考方案7】:不是自定义字体,而是另一种更改导航项字体的方法。创建一个名为 design_navigation_item.xml
的布局。
<android.support.design.internal.NavigationMenuItemView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
android:layout_
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 更改为所需的字体。
【讨论】:
在哪里使用这个布局?【参考方案8】:回答有点晚了,但我找到了一种更清洁的方法,所以我想分享一下。
制作自定义视图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://***.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_
android:layout_
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
你可以走了!这样,您可以保持主要活动更清洁。
这是一个屏幕截图:
【讨论】:
我真的很喜欢这种方法,但它似乎不起作用,因为 NavigationMenuItemView 不继承自 TextView。是否缺少步骤?【参考方案9】:我已经重构了 @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】:对于那些使用@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);
也许它对某人有帮助。
【讨论】:
【参考方案11】:我真的很喜欢“吐火龙”的解决方案,但我没有得到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>
【讨论】:
【参考方案12】:在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"
/>
【讨论】:
【参考方案13】:这是另一种方法:
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);
【讨论】:
【参考方案14】: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);
【讨论】:
欢迎来到 Stack Overflow。请在您的答案中添加更多信息。不要只是转储代码。【参考方案15】: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);
【讨论】:
添加一些信息而不是只转储代码并格式化答案。以上是关于如何为 NavigationView 中的项目设置自定义字体?的主要内容,如果未能解决你的问题,请参考以下文章
如何处理NavigationView PaneFooter设置项单击
如何为 IntelliJ IDEA 中的所有项目设置默认项目位置?