全局字體設置 || 老年模式


通過設置字號,同步改變全局的字體。
長文干貨,建議點贊收藏。

實現方式有多種:

1.通過AppTheme主題設置

通過配置不同的字體主題,然后設置更換主題來改變全局的字體樣式,主題中可配置自定義字體大小等;xml布局中也需要添加style主題,設置主題后需要recreate ui,體驗不好。

2.修改系統fontScale,縮放字體大小

百度字體設置等,很多方案都是這種,主要重寫Application的onConfigurationChanged監聽系統字體大小變化,然后重啟app或者activity才會刷新同步,而且對數值不可控,還會影響一些默認配置以及存在適配問題,不作深入研究,直接拋棄。

3.自定義view

每個view中會有一個監聽,修改字體后觸發監聽更改字體;每次創建讀取本地緩存,設置字體;需要在顯示的地方更換為自定義的view。
看着略顯麻煩,但是不得不說,很靈活,並且方便擴展需求,但是麻煩,除了自定義view還得處理監聽。

4.自定義binding屬性「最終選擇」

類似於方案3,在binding方法中去初始化跟設置字體,擴展性好,並且不需要替換textview,流程就跟方案1類似,需要在xml中配置類型屬性,然后binding會自動根據配置的屬性去設置字體。

全局字體設置之自定義binding

先看效果圖,在設置頁面,通過設置字體的類型,然后保存下來,同時刷新binding監聽,更改所有顯示的textview字體大小。

並且可以方便擴展,比如tablayout中選中放大等效果。

 

 

 

首先新建字體設置工具類。

用於存儲字體類型,獲取字體樣式。

class FontUtils {

    companion object {
        private const val TAG = "FontUtils"
        private const val KEY_APP_FONT = "key_app_font"
        //標准字體
        const val NORMAL_FONT_STYLE = "normal_text_size"
        //大號字體
        const val BIG_FONT_STYLE = "big_text_size"
        //特大字體
        const val LARGE_FONT_STYLE = "large_text_size"

        private val instance by lazy { FontUtils() }
        fun getFontUtils(): FontUtils = instance
    }

    /** 字體樣式類型  */
    fun getAppFontStyle(): String {
        return SPUtils.getInstance().getString(KEY_APP_FONT, NORMAL_FONT_STYLE)
    }

    fun saveAppFontStyle(appFontStyle: String?) {
        SPUtils.getInstance().put(KEY_APP_FONT, appFontStyle)
    }

    /** 獲取模型  */
    fun getFontVo(fontType: String?): FontBean? {
        val fontStyle: String = getAppFontStyle()
        val fontVoList: List<FontBean>? = getRawFileList(fontStyle)
        return if (CollectionUtils.isNotEmpty(fontVoList)) {
            // LogUtils.i(TAG, "getFontVo-- fontVo:" + GsonUtils.toJson(fontVo));
            getFontByType(fontVoList, fontType)
        } else null
    }
    /**
     * 解析模型
     * @param fontType 具體字號類型
     * */
    private fun getFontByType(fontVoList: List<FontBean>?, fontType: String?): FontBean? {
        return CollectionUtils.find(
            fontVoList
        ) {
            it != null && StringUtils.equals(it.fontType, fontType)
        }
    }
    /** 字體模型  */
    private fun getRawFileList(fontStyle: String?): List<FontBean>? {
        return when {
            StringUtils.equals(NORMAL_FONT_STYLE, fontStyle) -> {
                getFontListByRaw(R.raw.font_normal)
            }
            StringUtils.equals(BIG_FONT_STYLE, fontStyle) -> {
                getFontListByRaw(R.raw.font_big)
            }
            StringUtils.equals(LARGE_FONT_STYLE, fontStyle) -> {
                getFontListByRaw(R.raw.font_large)
            }
            else -> getFontListByRaw(R.raw.font_normal)
        }
    }
    /** 讀取本地模型 路徑 /res/raw/resId */
    private fun getFontListByRaw(@RawRes resId: Int): List<FontBean>? {
        return GsonUtils.fromJson<List<FontBean>>(
            ResourceUtils.readRaw2String(resId),
            object : TypeToken<List<FontBean>>() {}.type
        )
    }

}
View Code

下面是字體模型截圖,類似方案1中的字體主題,分別對應設置頁面的標准字體,大號字體,特大字體,可隨意擴展。

xml中設置的字體類型就來自模型中讀取的數值

 

接下來就是自定義binding屬性,具體設置方法。

@BindingAdapter(value = ["bindFontType"], requireAll = false)
fun TextView.setBindingFontStyle(fontType: String?) {
    if (TextUtils.isEmpty(fontType)) {
        LogUtils.i("setBindingFontStyle", "IS NULL")
        return
    }
    //去除字體內邊距
    includeFontPadding = false
    val fontVo: FontBean? = FontUtils.getFontUtils().getFontVo(fontType)
    if (fontVo != null) {
        textSize = fontVo.fontSize
    }
    val activity = ActivityUtils.getActivityByContext(context) as AppCompatActivity?
    if (activity != null) {
        LiveEventBus
            .get(LiveEventBusKey.FONT_STYLE, Int::class.java)
            .observe(activity, {
                val fontBean: FontBean? = FontUtils.getFontUtils().getFontVo(fontType)
                if (fontBean != null) {
                    textSize = fontBean.fontSize
                }
            })
    }
}
View Code

自定義binding方法中通過livedata注冊了一個監聽,所以跟方案3類似,實則是每一個textview都存在一個監聽,而livedata可以綁定生命周期,自動創建跟銷毀監聽,避免內存泄漏。

在xml中綁定設置的方法。

 

當布局創建時,會自動執行binding方法。
binding方法中會根據xml里的字體大小類型執行工具類中的getFontVo方法。
getFontVo方法回去讀取緩存在本地的字體類型,等於主題類型,從而讀取到具體的模型數據,拿到數據設置更新。
而binding方法中的監聽,綁定了當前的生命周期,所以當頁面銷毀或回收時會自動解除監聽。
只要xml中設置了自定義的binding屬性,就能同步修改更新,不影響原本的設置,如絲滑般柔順。

當然,因為是基於binding,所以項目得基於databinding才行。
因為我后面接觸過的項目都是databinding,並且也是主流。
如果不是就推薦方案3了,通過自定義view實現,大致流程也差不多。

碼字不易,喜歡就賞個贊吧。

自定義擴展

因為是binding,所以有時候在無法滿足需求的情況下可以額外擴展方法。
比如tablayout,實現一個選中字體放大的效果。

@BindingAdapter(value = ["bindFontType", "isCheck", "checkBuffSize"], requireAll = false)
fun TextView.setBindingFontStyle(fontType: String?, isCheck: Boolean, checkBuffSize: Int) {
    if (TextUtils.isEmpty(fontType)) {
        LogUtils.i("setBindingFontStyle", "IS NULL")
        return
    }
    //去除字體內邊距
    includeFontPadding = false
    //選中字體
    val fontVo: FontBean? = FontUtils.getFontUtils().getFontVo(fontType)
    if (fontVo != null) {
        textSize = if (isCheck) {
            fontVo.fontSize + checkBuffSize
        } else {
            fontVo.fontSize
        }
    }
    val activity = ActivityUtils.getActivityByContext(context) as AppCompatActivity?
    if (activity != null) {
        LiveEventBus
            .get(LiveEventBusKey.FONT_STYLE, Int::class.java)
            .observe(activity, {
                val fontBean: FontBean? = FontUtils.getFontUtils().getFontVo(fontType)
                if (fontBean != null) {
                    textSize = fontBean.fontSize
                }
            })
    }
}
View Code

很簡單,擴展了兩個屬性,一個是否選中,一個是增量。
只需要在xml中動態配置一下,然后通過邏輯控制就能同步設置。

 

這里有時候會碰到一些問題,比如tablayout例子。
如果是自定義tablayout,customview並沒有參與綁定,無法實現監聽的情況。
這里可以手動綁定一下解決,參數可以統一設置在binding方法中,也可以額外設置,不過最好統一管理,便於擴展跟維護。

/**
     * 配置選中/未選中狀態
     */
    private fun setTabLayoutSelected(tab: TabLayout.Tab, isCheck: Boolean) {
        val topicBinding: TabviewTopicBinding? = DataBindingUtil.getBinding(tab.customView!!)
        if (topicBinding != null) {
            topicBinding.setIsCheck(isCheck)//通過binding統一控制
            if (isCheck) {//也可以隨意
                topicBinding.tvTitle.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD))
            } else {
                topicBinding.tvTitle.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL))
            }
        }
    }
View Code

只要把工具類封裝好了,后續只需要設置binding屬性就行。
在需求跟擴展以及刷新效果來說,這個方案是很不錯的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM