如何在ActionBar标题中设置自定义字体?

269

如果可能的话,我该如何在ActionBar标题文本(仅限标题文本,而不是选项卡文本)中使用位于assets文件夹中的字体设置自定义字体? 我不想使用android:logo选项。

17个回答

425

您可以使用自定义的TypefaceSpan类来实现这一点。它比上述的customView方法更优越,因为它在使用其他操作栏元素(如扩展操作视图)时不会出现问题。

使用这样的类可能看起来像这样:

SpannableString s = new SpannableString("My Title");
s.setSpan(new TypefaceSpan(this, "MyTypeface.otf"), 0, s.length(),
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

// Update the action bar title with the TypefaceSpan instance
ActionBar actionBar = getActionBar();
actionBar.setTitle(s);

自定义的TypefaceSpan类需要传入您的Activity上下文和assets/fonts目录中字体的名称。它会加载文件并在内存中缓存一个新的Typeface实例。 TypefaceSpan的完整实现非常简单:

/**
 * Style a {@link Spannable} with a custom {@link Typeface}.
 * 
 * @author Tristan Waddington
 */
public class TypefaceSpan extends MetricAffectingSpan {
      /** An <code>LruCache</code> for previously loaded typefaces. */
    private static LruCache<String, Typeface> sTypefaceCache =
            new LruCache<String, Typeface>(12);

    private Typeface mTypeface;

    /**
     * Load the {@link Typeface} and apply to a {@link Spannable}.
     */
    public TypefaceSpan(Context context, String typefaceName) {
        mTypeface = sTypefaceCache.get(typefaceName);

        if (mTypeface == null) {
            mTypeface = Typeface.createFromAsset(context.getApplicationContext()
                    .getAssets(), String.format("fonts/%s", typefaceName));

            // Cache the loaded Typeface
            sTypefaceCache.put(typefaceName, mTypeface);
        }
    }

    @Override
    public void updateMeasureState(TextPaint p) {
        p.setTypeface(mTypeface);

        // Note: This flag is required for proper typeface rendering
        p.setFlags(p.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    }

    @Override
    public void updateDrawState(TextPaint tp) {
        tp.setTypeface(mTypeface);

        // Note: This flag is required for proper typeface rendering
        tp.setFlags(tp.getFlags() | Paint.SUBPIXEL_TEXT_FLAG);
    }
}

只需将上面的类复制到您的项目中,并按照上面所示在您的活动的onCreate方法中实现它。


20
好的回答。值得注意的是,你还展示了一种缓存字体元素的方法。 - Anand Sainath
6
很好。但有一点需要注意 - 如果底层 TextView 上设置了 textAllCaps 属性为 true(例如通过主题设置),则自定义字体将不会出现。当我将此技术应用于操作栏选项卡时,这对我造成了问题。 - James
5
请注意,该类的实现假定您将字体文件放在“assets/fonts/”目录下。如果您只是将.ttf/.otf文件放在资产中而不是子文件夹中,则应相应修改以下代码行:String.format("fonts/%s", typefaceName)。如果不遵循这个规则,您可能会遇到问题并收到此错误消息:java.lang.RuntimeException: Unable to start activity ComponentInfo{com.your.pckage}: java.lang.RuntimeException: native typeface cannot be made - Dzhuneyt
1
在启动应用程序的瞬间,会显示默认标题样式,大约1秒钟后会出现自定义样式。糟糕的用户界面... - DarkLeafyGreen
2
这是一个很棒的答案,对我帮助很大。但是我会建议将缓存机制移动到TypefaceSpan之外的自己的类中。我遇到了其他使用Typeface而没有span的情况,这使得我可以在这些情况下利用缓存。 - Justin
显示剩余11条评论

216

我同意这并不是完全支持的,但是这是我的做法。您可以为操作栏使用自定义视图(它将显示在您的图标和操作项之间)。我正在使用自定义视图,并已禁用原生标题。所有我的活动都继承自一个单一的活动,该活动在onCreate中有以下代码:

this.getActionBar().setDisplayShowCustomEnabled(true);
this.getActionBar().setDisplayShowTitleEnabled(false);

LayoutInflater inflator = LayoutInflater.from(this);
View v = inflator.inflate(R.layout.titleview, null);

//if you need to customize anything else about the text, do it here.
//I'm using a custom TextView with a custom font in my layout xml so all I need to do is set title
((TextView)v.findViewById(R.id.title)).setText(this.getTitle());

//assign the view to the actionbar
this.getActionBar().setCustomView(v);

我的布局xml文件(在上面的代码中为R.layout.titleview)如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" >

<com.your.package.CustomTextView
        android:id="@+id/title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="10dp"
            android:textSize="20dp"
            android:maxLines="1"
            android:ellipsize="end"
            android:text="" />
</RelativeLayout>

1
这对标题来说很好用,但是如果你想要一个标题和选项卡,它会将自定义视图放置在选项卡右侧,而不是像标题一样居左。我希望能够改变实际标题。 - draksia
2
很棒的解决方案。如果您需要一种自定义文本视图类,允许在XML中指定字体,请尝试我的!http://github.com/tom-dignan/nifty--非常容易。 - Thomas Dignan
我在该解决方案中没有看到任何字体的引用。 - AlikElzin-kilaka
正如我在答案中提到的那样,我对TextView进行了子类化,并在上面的布局中使用了该类。在我的自定义TextView中,我重写了setTypeface方法,就像这个答案中所示:https://dev59.com/L2ox5IYBdhLWcg3wzXil - Sam Dozor
2
这个方法可以实现,但是工作量太大了。而且:你会失去一些标准标题的功能,比如当单击图标时突出显示...自定义标题不应该用于重新创建标准标题布局,只能用于更改字体... - Zordid
显示剩余4条评论

153
int titleId = getResources().getIdentifier("action_bar_title", "id",
            "android");
    TextView yourTextView = (TextView) findViewById(titleId);
    yourTextView.setTextColor(getResources().getColor(R.color.black));
    yourTextView.setTypeface(face);

2
这应该是对这个问题的首选答案。非常好用,"action_bar_subtitle"也可以!谢谢! - Zordid
22
如果安卓开发者在新版本中将资源ID从“action_bar_title”更改为其他名称,那么这些代码都将无法正常工作。这就是为什么它的投票不太高的原因。 - Diogo Bento
6
适用于API版本大于3.0的应用,但在AppCompat 2.x上不能使用。 - Aman Singhal
1
这确实改变了字体和其他一些东西。但是当我进入下一个活动并按返回键时,字体会恢复原样。我猜这与ActionBar属性有关。 - Pranav Mahajan
11
@Digit:这对于“Holo主题”非常有效,但是对于“Material主题”(Android L)则无法奏效。可以找到titleId,但textview为空..有什么解决办法吗?谢谢! - Michael D.
显示剩余4条评论

42

Android Support Library v26 + Android Studio 3.0开始,这个过程变得轻而易举!

按照以下步骤更改工具栏标题的字体:

  1. 阅读可下载字体并从列表中选择任何字体(我的推荐),或者根据XML中的字体res > font中加载自定义字体
  2. res > values > styles中,粘贴以下内容(在这里发挥你的想象力!

    <style name="TitleBarTextAppearance" parent="android:TextAppearance">
        <item name="android:fontFamily">@font/your_desired_font</item>
        <item name="android:textSize">23sp</item>
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">@android:color/white</item>
    </style>
    
  3. 按照下面所示,在您的工具栏属性中插入一行app:titleTextAppearance="@style/TextAppearance.TabsFont"以换行。

  4. <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:titleTextAppearance="@style/TitleBarTextAppearance"
        app:popupTheme="@style/AppTheme.PopupOverlay"/>
    
  5. 享受自定义Actionbar标题字体样式!!


3
这对工具栏非常有用。有没有一种方法可以在整个应用程序中实现这种效果,就像在新活动中拥有默认应用程序栏时一样? - Jordan H
这也适用于折叠标题应用程序:collapsedTitleTextAppearance="@style/Style_Title" - Firas Shrourou
有趣的是,这对于<item name="android:ellipsize">none</item>无效。 - CaptainCrunch

14

Calligraphy库可以通过应用程序主题设置自定义字体,这也适用于操作栏。

<style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
<item name="android:textViewStyle">@style/AppTheme.Widget.TextView</item>
</style>

<style name="AppTheme.Widget"/>

<style name="AppTheme.Widget.TextView" parent="android:Widget.Holo.Light.TextView">
   <item name="fontPath">fonts/Roboto-ThinItalic.ttf</item>
</style>

只需要将 Calligraphy 附加到您的 Activity 上下文即可激活:

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(new CalligraphyContextWrapper(newBase));
}

默认的自定义属性是fontPath,但您可以通过在应用程序类中使用CalligraphyConfig.Builder来初始化自己的自定义属性路径。不建议使用android:fontFamily


此解决方案需要最低API 16。 - Sami Eltamawy
根据项目的构建文件,minSdk为7,但我正在一个minSdk为18的项目中使用它,并没有进一步进行检查。使用了哪种有问题的方法? - thoutbeckers
它的最小API为7,仅示例为API16。它支持appcompat-v7+。 - Chris.Jenkins

11
    ActionBar actionBar = getSupportActionBar();
    TextView tv = new TextView(getApplicationContext());
    Typeface typeface = ResourcesCompat.getFont(this, R.font.monotype_corsiva);
    RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
            RelativeLayout.LayoutParams.MATCH_PARENT, // Width of TextView
            RelativeLayout.LayoutParams.WRAP_CONTENT); // Height of TextView
    tv.setLayoutParams(lp);
    tv.setText("Your Text"); // ActionBar title text
    tv.setTextSize(25);
    tv.setTextColor(Color.WHITE);
    tv.setTypeface(typeface, typeface.ITALIC);
    actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
    actionBar.setCustomView(tv);

太棒了!这个工作完美无缺,我如何将此应用栏放置到中心位置? - Prasath
1
运行得很好...只需将 typeface.ITALIC 替换为 Typeface.ITALIC 即可消除静态成员警告。 - Zain
1
05/02/2021 对我来说唯一有效的解决方案。谢谢,伙计。 - GLHF

10

这种方法很丑陋,但你可以像这样做(因为action_bar_title被隐藏了):

    try {
        Integer titleId = (Integer) Class.forName("com.android.internal.R$id")
                .getField("action_bar_title").get(null);
        TextView title = (TextView) getWindow().findViewById(titleId);
        // check for null and manipulate the title as see fit
    } catch (Exception e) {
        Log.e(TAG, "Failed to obtain action bar title reference");
    }

这段代码适用于Gingerbread版本之后的设备,但是也可以轻松地扩展到与ActionBar Sherlock一起使用。

P.S. 根据@pjv的评论,有一种更好的方法来查找ActionBar标题ID。

final int titleId = 
    Resources.getSystem().getIdentifier("action_bar_title", "id", "android");

4
我更喜欢dtmilano在https://dev59.com/omTWa4cB1Zd3GeqPCFBA中的答案,它与原来的答案类似但稍微更具未来性。 - pjv
1
@pjv - 同意。看起来不那么“hacky”。我修改了我的答案。 - Bostone
1
所以问题是关于自定义字体的。这回答了如何获取默认操作栏的文本视图。 - AlikElzin-kilaka
@kilaka - 这个想法是,如果你获取了文本视图设置自定义字体将是微不足道的。虽然这是一个旧帖子,但我认为twaddington的答案更受欢迎。 - Bostone

8

以下代码适用于所有版本。我已在安装Gingerbread和JellyBean的设备上进行了测试。

 private void actionBarIdForAll()
    {
        int titleId = 0;

        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB)
        {
            titleId = getResources().getIdentifier("action_bar_title", "id", "android");
        }
        else
        {
          // This is the id is from your app's generated R class when ActionBarActivity is used for SupportActionBar

            titleId = R.id.action_bar_title;
        }

        if(titleId>0)
        {
            // Do whatever you want ? It will work for all the versions.

            // 1. Customize your fonts
            // 2. Infact, customize your whole title TextView

            TextView titleView = (TextView)findViewById(titleId);
            titleView.setText("RedoApp");
            titleView.setTextColor(Color.CYAN);
        }
    }

这对我在ActionBar和AppCompat ActionBar上都有效。但是后者只有在我尝试在onCreate()之后找到标题视图时才起作用,因此例如将其放置在onPostCreate()中就可以解决问题。 - Harri

8

使用支持库中的新工具栏,可以设计自己的操作栏或使用以下代码

膨胀TextView并不是一个好选择,尝试使用Spannable StringBuilder。

Typeface font2 = Typeface.createFromAsset(getAssets(), "fonts/<your font in assets folder>");   
SpannableStringBuilder SS = new SpannableStringBuilder("MY Actionbar Tittle");
SS.setSpan (new CustomTypefaceSpan("", font2), 0, SS.length(),Spanned.SPAN_EXCLUSIVE_INCLUSIVE);
actionBar.setTitle(ss);

复制下面的类

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

}

3

我刚在onCreate()函数中执行了以下操作:

TypefaceSpan typefaceSpan = new TypefaceSpan("font_to_be_used");
SpannableString str = new SpannableString("toolbar_text");
str.setSpan(typefaceSpan,0, str.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
getSupportActionBar().setTitle(str);

我正在使用支持库,如果您没有使用它们,我猜您应该切换到getActionBar()而不是getSupportActionBar()。

在Android Studio 3中,您可以按照此说明https://developer.android.com/guide/topics/ui/look-and-feel/fonts-in-xml.html添加自定义字体,然后在“font_to_be_used”中使用您新添加的字体。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接