当时组内临时接到一个换字体库的需求,这个需求相对简单,因为手头有其他事情,同时之前也没换过字体库,就交给了同事去做了;现在有时间就好好充实下自己 ( 我写的也未必全对,如有不足可直接提出,相互探讨)

在正式开始以前,你首先需要告知产品经理和设计师,因为引入新的字体库存在版权问题,需要对方授权方可使用,否则涉及侵权;如果产品确定一定要改的话,请产品和设计提供相关字体库

    • 业务需求&实现
      • 业务需求
      • 业务实现
        • 静态实现
        • 动态实现
    • 基础认知
    • 厂商字体
      • 小米(Misans)
      • 华为(HarmonyOS Sans)
      • OPPO(OPPO SANS)
    • 原理兴趣

开篇前没想到写一篇基础版的字体库相关内容,用了小俩天时间,越写越多,也参考了十几篇blog,有新有旧,希望该篇对你有所帮助

Look:不知道你在看此篇时,是否有了解过字体库相关内容,我先行说一下,我在求知中遇到的问题

Android的默认字体到底是什么?为什么有的说是 Source Han Sans(思源),有的说是 Roboto,有的说是 DroidSans、DroidSansFallback

关于这个问题,我翻了一堆文章,答案都东拼西凑、大同小异,并没有一个比较完善的解释,最后还是去外面找到一个合理的解释,我来统一整合一下吧(Hint:如果你想了解更多,也有一些个人兴趣,可前往基础认知中继续了解下,欢迎探讨~)

首先 Source Han Sans(思源)RobotoDroidSans、DroidSansFallback 字体、字体库都是存在的,且都是存在于Android系统中的!

其中 Source Han Sans(思源) 适用于中文环境 & 中日韩三国,Roboto适用于英文&数字, DroidSans、DroidSansFallback适用于中日韩以外的国家 或 作为备用(默认)字体;(故知道国内用的是思源字体库+Roboto字体库即可

业务需求&实现

引入、以及使用新的字体库,都很简单,如果只是为了实现业务需要的话,只看该段即可;如果有兴趣,且时间充足的话可以大致过一下~

业务需求

因为是UI的设计需求,我们主要看设计图相关数据信息即可

UI效果图

font-family(字体库): Roboto-Medium;

Hint:根据设计图可以看出使用到的主要是 Roboto字体中的 中等加粗字体

业务实现

关于字体库业务的实现方式,我看了一下实现方式,主要有静态实现(xml)和动态实现(代码设置),具体使用哪种方式取决于自己和业务场景的使用

因为俩种先方式针对于字体库的存储位置有所不同,故开设求知小课堂简单说明res、assets同异

相同点:两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制

不同点:

  • 引用资源的方式不同:res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。
  • 处理方式不同
  • 子目录不同:res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

根据涉及提供的字体库,解压后放在AndroidStudio内可以直接看预览效果

综合实现效果

俩种实现方式的效果是统一的,只是静态实现支持预览,故统一效果图;

视图效果分别对应以下三类(我其实看不出太大区别,可能只有设计比较容易辨别)

  • 默认字体
  • Roboto 普通字体
  • Roboto 加粗字体

静态实现

静态实现的优势在于使用方便,可以直接设置属性,同时支持直接观察设置效果;不足之处在于产品要求某些业务场景,需要动态切换控件的字体效果(千人千面)

  1. res - 创建 font 文件夹用于存放字体库
  2. 关于字体库的命令需要符合命名规范(对比动态实现命名有所不同)

错误命名

正确使用

xml 中引用字体库

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginTop="20dp"android:gravity="center"android:text="0.00" /><TextViewandroid:id="@+id/tv_text1"android:layout_width="wrap_content"android:layout_height="40dp"android:fontFamily="@font/roboto_regular"android:layout_marginTop="10dp"android:gravity="center"android:text="0.08" /><TextViewandroid:id="@+id/tv_text2"android:layout_width="wrap_content"android:layout_height="40dp"android:fontFamily="@font/roboto_medium"android:layout_marginTop="10dp"android:gravity="center"android:text="66.66" /></LinearLayout>

预览效果

动态实现

动态实现的方式同静态实现稍有不同,首先是资源存储的地方,其次是设置字体库的方式

  1. 创建assets文件夹

    AndroidStudio 会自动提示
  2. assets 文件夹下创建 fonts 文件夹,主要用于存储字体库
  3. 字体库一般为 ttf 文件,我们将字体与放置于此即可,总体如下

调用方式

常规设置

package com.example.kotlindemoimport android.annotation.SuppressLintimport android.graphics.Typefaceimport android.os.Bundleimport android.widget.TextViewimport androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle" />) {super.onCreate(savedInstanceState)setContentView(R.layout.want_text_display)var t1 = findViewById<TextView>(R.id.tv_text1)val regular = Typeface.createFromAsset(assets,"fonts/Roboto-Regular.ttf")t1.typeface = regular;}}

单控件一般采用同一个字体库样式,这里是俩个控件的设置方式(设置方式相同),只所以多写一份是因为我引用了俩个字体库的样式,一个为roboto普通字体,一个为roboto加粗字体

package com.example.kotlindemoimport android.annotation.SuppressLintimport android.graphics.Typefaceimport android.os.Bundleimport android.widget.TextViewimport androidx.appcompat.app.AppCompatActivityclass MainActivity : AppCompatActivity() {@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.want_text_display)var t1 = findViewById<TextView>(R.id.tv_text1)var t2 = findViewById<TextView>(R.id.tv_text2)val regular = Typeface.createFromAsset(assets,"fonts/Roboto-Regular.ttf")val medium = Typeface.createFromAsset(assets,"fonts/Roboto-Medium.ttf")t1.typeface = regular;t2.typeface = medium;}}

want_text_display

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center_horizontal"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginTop="20dp"android:gravity="center"android:text="0.00" /><TextViewandroid:id="@+id/tv_text1"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginTop="10dp"android:gravity="center"android:text="0.08" /><TextViewandroid:id="@+id/tv_text2"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_marginTop="10dp"android:gravity="center"android:text="66.66" /></LinearLayout>

留作备忘

我之前翻了一篇blog,看到说要设置ElegantTextHeight属性,否则高度会发生变化,但是我并未遇到相关场景,故再次留作备忘

// 应用字体var typeFace = Typeface.createFromAsset(mContext.getAssets(),"fonts/sourcehanserifcn_regular.otf")//这个不可缺少,不加tvtext高度变大很多很不美观tvtext.setElegantTextHeight(true)tvtext.setTypeface(typeFace)

基础认知

Android 拥有着属于自己的默认字体库,不过因为国内厂商较多,大多都会定制化字体,所以显示效果各有不同,大多数时候字体并未完全统一

针对于我们常见的中文、英文、数字使用的字体库也有所不同

Android 默认字体

  • 中文字体:Source Han Sans / Noto(思源黑体)

维基百科:思源黑体(英语:Source Han Sans,“思源”一词来自于成语“饮水思源”)是Adobe与Google所领导开发的开源字体家族,1.001及更早版本以Apache 2.0许可证许可,而1.002及更新版本则使用SIL开源字体授权,属于无衬线黑体。思源黑体于2014年7月16日首次发布,支持繁体中文、简体中文、日文及韩文,并且各有7种字体粗细。公开之时为当时涵盖字符数量最多的字体,44,666 个字符分属于 65,535 个字形中,此为OpenType字体技术的极限。

  • 数字&英文字体:Roboto

小课堂

DroidSansFallback思源黑体是两种不同的字体,它们在 Android 系统中具有不同的作用和应用场景。

DroidSansFallbackDroidSansFallback 是 Android 系统中的一个字体库,用于支持多语言和字符集的显示。它包含了广泛的 Unicode 字符覆盖范围,包括了一些不常见的字符和语言的字形。当系统无法找到适当的字体来显示特定字符时,它会使用 DroidSansFallback 来作为回退字体。这使得 Android 能够显示多种语言和符号,即使默认字体库(如 Roboto)没有包含对应的字符。

以下是Android安卓系统默认的几种字体:

  • Clockopia.ttf 系统默认待机时钟字体;
  • DroidSansFallback.ttf 系统默认中文字体;
  • DroidSans.ttf 系统默认英文字体;
  • DroidSans-Bold.ttf 系统默认英文粗字体;

思源黑体:思源黑体是一种由 AdobeGoogle 共同开发的开源字体,用于支持中日韩(CJK)语言的显示。它包含了汉字、日文假名和韩文字符,具有广泛的覆盖范围。思源黑体在设计上注重保持字形的清晰度和可读性,同时适应不同的屏幕尺寸和分辨率。在一些中日韩语言环境下,Android 系统会使用思源黑体作为默认的字体来显示相应的文字内容。

不论我们使用哪种字体库,一般字体库都会提供字体的多种样式,例如常规、加粗等,例如 Regular、Italic、Bold、Bold-italic、Light、Light-italic、Thin、Thin-italic、Condensed regular、Condensed italic、Condensed bold、Condensed bold-italic

作为队友,有必要了解下 IOS 默认字体

  • 中文字体:PingFang SC
  • 数字&英文字体:.SF UI Text、.SF UI Display

默默 copy 俩张图

以中文字体来做对比,可以看出平方对比思源黑体,笔画更细、字怀更大、字体使用弧度更少,偏旁所占字面更小(说实话,还得设计来看,我基本看不出什么太大区别…)。

关于Roboto字体的一些补充介绍

这部分内容偏官方简介,仅做了解即可,无需细读,以下介绍均针对于Android客户端

字体特点

  • 清晰的字形:Roboto字体有着非常清晰的字形,这使得它在阅读和书写时非常舒适。
  • 可读性强:Roboto字体具有很好的可读性,这意味着在大多数应用程序中它都能很好地适用。
  • 平衡的间距:Roboto字体在行与行之间的间距上也有很好的平衡。这意味着文本在屏幕上的布局非常均匀,看起来非常整齐。
  • 广泛的可用性:Roboto字体是一种开源字体,因此它在Android开源项目中被广泛使用。这使得它在各种设备和屏幕分辨率上都能很好地适用。

字体优点

  • 看起来专业:使用Roboto字体可以使应用程序看起来更加专业化。它的清晰字形和平衡的间距使得文本在屏幕上看起来更加清晰。
  • 提高可读性:由于Roboto字体的可读性很好,因此在阅读和书写时非常舒适,这也意味着应用程序的可用性得到了提高。
  • 减少屏幕分辨率变化:由于Roboto字体在各种设备和屏幕分辨率上都能很好地适用,因此应用程序在适应不同屏幕大小时不会产生过大的影响。
  • 延长设备电池寿命:一些研究表明,使用Roboto字体可以延长设备电池寿命。这是因为较暗的字体可以减少屏幕亮度的需求,从而减少了电池的消耗。

字体不足

  • 应用程序的可读性降低:如果应用程序中的文本使用了不太一样的字体,那么可能会导致应用程序的可读性降低。
  • 应用程序的可用性降低:如果应用程序中的文本使用了Roboto字体,而设备屏幕分辨率较低,那么可能会导致应用程序的可用性降低。
  • 设备电池消耗增加:有一些研究表明,使用Roboto字体可能会导致设备电池消耗增加。这是因为较暗的字体可能会导致屏幕亮度的需求增加,从而增加了电池的消耗。

厂商字体

Anddroid本身的定制性较强,早期换字体的时候基本上需要root,如今手机自带这个功能,各厂商也有了足够的权限,所以在看到大家对系统默认字体不是很喜欢时,就致力研发自家的字体;针对国内的厂商大多都有自定的字体,例如 MisansOPPO SANSHarmonyOS Sans 等一些手机厂商默认的字体。这些字体不仅支持中文显示,连西文(如英文)都能完美显示。

在一篇文章内看到介绍了 各大手机厂商的默认字体,此处仅做一些关键信息摘要

这位朋友说字体好不好,还需注意这三个方面:字符数要多(照顾部分生僻字)、字重齐全(粗细均匀)、OpenType支持(字符根据情况自动调整,使其看起来更加美观)。

我们来看看主流的这三款字体哪个更好看吧!这里再说明一下,这三款字体都是汉仪定制字体,因此字体不全是手机厂商定制。

小米(Misans)

针对于MIUI13里面宣传的全新系统字体MiSans,全方位优化中英文及数字设计,字形简约,视觉清晰,阅读舒适。

其实这个MiSans算是黑体的变种。虽然也是黑体,但是在一些细节处能看出是真下了功夫

  • 笔型平直有力
  • 设计更加简约
  • 减少视觉负担
  • 更有利于屏幕显示
  • 支持OpenType功能

MiSan一出,尤其是在一些长文和标点符号比较多的场景下,明显所谓的高级感更强,而鸿蒙字体与其相比就显得普通了一些。

华为(HarmonyOS Sans)

  • 支持更多的语言;
  • 对斜体和繁体中文有专门的加强字体;
  • 中英文混排、文字和标点符号混排感觉也是相当优秀,不会出现很生硬的错行,但是字重比小米MiSans要少,只有6种字重。

OPPO(OPPO SANS)

  • 全新骨架
  • 中宫自然舒适
  • 结构稳定
  • 字形舒展
  • 强化了屏幕显示的易读性和美观性

原理兴趣

Android字体工作原理

android字体由android 2D图形引擎skia实现,并在ZygotePreloading classes中对系统字体进行load

相关文件有:skTypeface.cppskFontHost_android.cpp,其中后者是skia针对android平台字体实现的port。主要的变量有:

struct FontInitRec {const char* fFileName;const char* const*fNames; // null-terminated list};struct FamilyRec {FamilyRec*fNext;SkTypeface* fFaces[5];};//uint32_t gFallbackFonts[SK_ARRAY_COUNT(gSystemFonts)+1];load_system_fonts()@skFontHost_android.cpp 

load系统中所有的字体并给每种字体分配唯一的ID,并将字体分为两种:FamilyFontsFallbackFontsskPaint通过应用程序设置的字体(Typeface)所对应的ID最终实现字符的显示。

替换Android默认的汉字字体

android系统中,DroidSans是默认字体,只包含西方字符,应用程序默认情况下都会调用它,而DroidSansFallback包含了东亚字符,当需要显示的字符在DroidSans字体中不存在(如:汉字)时,即没有对应编码的字符时,系统会到DroidSansFallback中去找相应编码的字符,如果找到,则使用DroidSansFallback字体来显示它,如果仍找不到该编码对应的字符,则无法在屏幕上显示该字符。

为android系统添加一种默认字体,类似“sans”,“serif”,“monospace

在android系统中,默认的中文字体只有一种:DroidSansFallback.ttf,如果想在android应用程序中随意设置想要的中文字体,除了在应用程序中通过assets目录引入字体文件外,还可以通过增加android默认字体的方式来实现。添加步骤大致如下:

  1. frameworks/base/data/fonts 目录下添加字体文件,例如 Driod-kaishu.ttf;
  2. skia中增加楷书这一字体,需要修改的文件主要有skFontHost.cpp、skTypeface.cpp、Typeface.java等;
  3. java层添加楷书字体相关API,需要修改的文件主要有typeface.java和textview.java;
  4. 编译SDK
  5. 将新生成的sdk导入eclipse,在eclipse中即可通过setTypeface(Typeface.KAISHU)android:typeface=(“kaishu”)两种方式设置自己添加的字体。