需求
最近做完了新功能,突发奇想,想着如何处理app的动态切换字体
方案
方案1:通过反射机制,修改Typeface类的字体库引用
1 | object FontUtils { |
不过现有项目都是kotlin写的,不太想用反射
方案2:通过修改特定的View或者自定义TextView
工作量太大,而且效果不好.
方案3:通过第三方库
Calligraphy
这个库维护到现在5年多了,而且每年都有更新,使用也比较简单.当然作者现在已经不维护了,但是建议我们使用另一个大佬维护的3.0版本Calligraphy3
实现
接入
2.0版本
1 | api 'uk.co.chrisjenx:calligraphy:2.3.0' |
3.0版本
1 | api 'io.github.inflationx:calligraphy3:3.0.0' |
因为我是在library模块引入的,所以使用的api方式
初始化
我们可以选择在自定义的Application中初始化,也可以在LauncherActivity中,看个人喜好.
2.0版本
1 | private fun initTypeFace() { |
3.0版本
1 | ViewPump.init(ViewPump.builder() |
在onCreate()方法中调用initTypeFace()即可
这段代码是初始化我们的字体,其实在这里我们就可以选择自己喜欢的字体了,不过选完以后字体就固定了,这与我们的需求不符.
我们先去自己的BaseActivity中处理一下
2.0版本
1 | override fun attachBaseContext(newBase: Context?) { |
3.0版本
1 | override fun attachBaseContext(newBase: Context?) { |
因为要让字体全局生效,必然要在每个Activity的attachBaseContext()方法中绑定我们的框架,所以BaseActivity是最好的选择了.
其实到这里,我们的三方字体就能正常显示了,当然,如果你想针对单个控件设置特别的字体,你可以这样做
1 | <RadioButton |
只需要给文本控件设置fontPath就好了
高级操作
起初,我们的字体是这样的
我们的目的是要让用户自己选择显示什么样的字体,比如这样:

因此我们还需要其他操作,新建一个FontActivity,在其伴生类中初始化所有字体
1 | companion object { |
FontStyle是一个data class
1 | data class FontStyle(val name:String,val tag :String,val path:String,var inUse:Boolean =false) |
用于记录字体信息,包括字体名称,tag标记,以及字体路径,字体的选取与否保存在SharedPreference当中
1 | val string = PreferenceUtil.readString("fonts", "fontPath", "fonts/FounderBlack.ttf") |
遍历字体集合动态生成RadioButton,加字体tag设置给radioButton,并将已选中的字体选中
通过
1 | CalligraphyUtils.applyFontToTextView(radioButton, Typeface.createFromAsset(assets, font.path)) |
方法动态设置字体,然后我们就得到了上面的显示效果了
选中后点击确定
1 | val tag = findViewById<RadioButton>(binding.rg.checkedRadioButtonId)?.tag |
通过选中的radiobutton的tag来判断我们想要换的是什么字体,将它的路径,name tag都保存起来,然后调用application的finishAll()方法关闭所有的Activity,最后再次启动我们的LauncherActivity,我们的APP就重新启动并加载了新的字体了
效果对比
切换前

切换后

