• 全局字体设置 || 老年模式


    通过设置字号,同步改变全局的字体。
    长文干货,建议点赞收藏。

    实现方式有多种:

    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属性就行。
    在需求跟扩展以及刷新效果来说,这个方案是很不错的。

  • 相关阅读:
    HTTP/2之服务器推送(Server Push)最佳实践
    QQ空间掉帧率优化实战
    “战术竞技类”外挂打击已开始!揭秘腾讯We Test游戏安全服务新动作!
    你知道android的MessageQueue.IdleHandler吗?
    Hi,腾讯WeTest联合Unity官方打造的性能分析工具UPA,今日全新发布!
    一次触摸,Android到底干了啥
    面向亿万级用户的QQ一般做什么?——兴趣部落的Web同构直出分享
    双十一临近,怎样让买家流畅地秒杀? ——腾讯WeTest独家开放电商产品压测服务
    mybatis-generator 覆盖新增XML
    Jvm 虚拟机
  • 原文地址:https://www.cnblogs.com/LiuZhen/p/15185913.html
Copyright © 2020-2023  润新知