Loading... #需求 #####最近做完了新功能,突发奇想,想着如何处理app的动态切换字体 # 方案 ####方案1:通过反射机制,修改Typeface类的字体库引用 ```kotlin object FontUtils { fun setDefaultFont(context: Context,staticTypefaceFieldName:String,fontAssetName:String){ val font = Typeface.createFromAsset(context.assets, fontAssetName) replaceFont(staticTypefaceFieldName,font) } fun replaceFont(staticTypefaceFieldName: String, newTypeface: Typeface) { try { val staticField = Typeface::class.java.getDeclaredField(staticTypefaceFieldName) staticField.isAccessible = true staticField.set(null, newTypeface) } catch (e: NoSuchFieldException) { e.printStackTrace(); } catch (e: IllegalAccessException) { e.printStackTrace(); } } } ``` 不过现有项目都是kotlin写的,不太想用反射 ####方案2:通过修改特定的View或者自定义TextView 工作量太大,而且效果不好. ####方案3:通过第三方库 [Calligraphy](https://github.com/chrisjenx/Calligraphy) 这个库维护到现在5年多了,而且每年都有更新,使用也比较简单.当然作者现在已经不维护了,但是建议我们使用另一个大佬维护的3.0版本[Calligraphy3](https://github.com/InflationX/Calligraphy) #实现 ####接入 #####2.0版本 ```java api 'uk.co.chrisjenx:calligraphy:2.3.0' ``` #####3.0版本 ```java api 'io.github.inflationx:calligraphy3:3.0.0' api 'io.github.inflationx:viewpump:1.0.0' ``` 因为我是在library模块引入的,所以使用的api方式 ####>初始化 我们可以选择在自定义的Application中初始化,也可以在LauncherActivity中,看个人喜好. #####2.0版本 ```kotlin private fun initTypeFace() { val path = PreferenceUtil.getPreference("fonts")!!.getString("fontPath", "fonts/FounderBlack.ttf") CalligraphyConfig.initDefault(CalligraphyConfig.Builder() .setDefaultFontPath(path) .setFontAttrId(R.attr.fontPath) .build() ) } ``` #####3.0版本 ```kotlin ViewPump.init(ViewPump.builder() .addInterceptor(CalligraphyInterceptor( CalligraphyConfig.Builder() .setDefaultFontPath(path) .setFontAttrId(R.attr.fontPath) .build())) .build()) ``` 在```onCreate()```方法中调用```initTypeFace()```即可 这段代码是初始化我们的字体,其实在这里我们就可以选择自己喜欢的字体了,不过选完以后字体就固定了,这与我们的需求不符.  #####我们先去自己的BaseActivity中处理一下 #####2.0版本 ```kotlin override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase)) } ``` #####3.0版本 ```kotlin override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(ViewPumpContextWrapper.wrap(newBase?:return)) } ``` #####因为要让字体全局生效,必然要在每个Activity的```attachBaseContext()```方法中绑定我们的框架,所以BaseActivity是最好的选择了. 其实到这里,我们的三方字体就能正常显示了,当然,如果你想针对单个控件设置特别的字体,你可以这样做 ```xml <RadioButton android:id="@+id/semibold" android:text="Bold" fontPath="fonts/mySemibold.ttf" android:padding="10dp" android:gravity="center" android:onClick="@{onclick}" android:textSize="14sp" android:layout_width="match_parent" android:layout_height="wrap_content" /> ``` #####只需要给文本控件设置fontPath就好了 ####高级操作 起初,我们的字体是这样的  #####我们的目的是要让用户自己选择显示什么样的字体,比如这样:  因此我们还需要其他操作,新建一个FontActivity,在其伴生类中初始化所有字体 ```kotlin companion object { private val fonts = arrayListOf( FontStyle("杨任东竹石粗体", "YRDBold", "fonts/YRDBold.ttf"), FontStyle("杨任东竹石细体", "TRDExtralight", "fonts/YRDExtralight.ttf"), FontStyle("站酷快乐体2016", "ZhankuHappy2016", "fonts/zhankuHappy2016.ttf"), FontStyle("站酷高端黑", "ZhankKuAdvancedBlack", "fonts/zhankuAdvancedBlack.ttf"), FontStyle("王汉宗魏碑体", "WHZ_WB", "fonts/WHZ_WB.ttf"), FontStyle("Oradano-Mincho名朝", "OradanoMincho", "fonts/Oradano-Mincho.ttf"), FontStyle("阿里汉仪智能黑体", "AliSmartBlack", "fonts/AliSmartBlack.ttf"), FontStyle("庞门正道标题体2.0增强版", "PMZD_Title", "fonts/PMZD-Title.ttf"), FontStyle("思源柔黑体", "GenJyuuGothic", "fonts/GenJyuuGothic-Medium.ttf"), FontStyle("方正黑体", "FounderBlack", "fonts/FounderBlack.ttf"), FontStyle("TanukiMagic麦克笔手绘", "TanukiMagic", "fonts/TanukiMagic.ttf") ) } ``` FontStyle是一个data class ```kotlin data class FontStyle(val name:String,val tag :String,val path:String,var inUse:Boolean =false) ``` 用于记录字体信息,包括字体名称,tag标记,以及字体路径,字体的选取与否保存在SharedPreference当中 ```kotlin val string = PreferenceUtil.readString("fonts", "fontPath", "fonts/FounderBlack.ttf") for (font in fonts) { val radioButton = RadioButton(this@FontsActivity) val layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT) layoutParams.gravity = Gravity.CENTER layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT layoutParams.bottomMargin = 30 radioButton.setPadding(10, 10, 10, 10) radioButton.layoutParams = layoutParams radioButton.tag = font.tag if (font.path == string) { font.inUse = true radioButton.isChecked = true mCheckRadioButton=radioButton } radioButton.text = font.name radioButton.maxLines = 1 radioButton.ellipsize = TextUtils.TruncateAt.END radioButton.textSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 12f, resources.displayMetrics) CalligraphyUtils.applyFontToTextView(radioButton, Typeface.createFromAsset(assets, font.path)) binding.rg.addView(radioButton) radioButton.setOnClickListener { if (radioButton==mCheckRadioButton) { return@setOnClickListener }else{ mCheckRadioButton?.isChecked=false radioButton.isChecked=true mCheckRadioButton=radioButton } } } ``` 遍历字体集合动态生成RadioButton,加字体tag设置给radioButton,并将已选中的字体选中 #####通过 ```kotlin CalligraphyUtils.applyFontToTextView(radioButton, Typeface.createFromAsset(assets, font.path)) ``` #####方法动态设置字体,然后我们就得到了上面的显示效果了 选中后点击确定 ```kotlin val tag = findViewById<RadioButton>(binding.rg.checkedRadioButtonId)?.tag for (font in fonts) { if (font.tag == tag) { if (font.inUse) { toastShort("字体正在使用当中...") } else { PreferenceUtil.writeString("fonts", "fontPath", font.path) PreferenceUtil.writeString("fonts", "fontName", font.name) PreferenceUtil.writeString("fonts", "fontTag", font.tag) BaseApplication.finishAll() startActivity(Intent(this@FontsActivity, LaunchActivity::class.java)) this@FontsActivity.overridePendingTransition(0, 0) } break } } ``` 通过选中的radiobutton的tag来判断我们想要换的是什么字体,将它的路径,name tag都保存起来,然后**调用application的```finishAll()```方法关闭所有的Activity,最后再次启动我们的LauncherActivity,我们的APP就重新启动并加载了新的字体了** #效果对比 ##切换前  ##切换后   最后修改:2021 年 07 月 22 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 1 如果觉得我的文章对你有用,请随意赞赏