老年模式 之 fontScale篇

之前整理过一篇全局字体设置 || 老年模式的文章,提到过4种方法,各有利弊。

最后推荐了方法4,自定义binding属性来实现。
这里扩展一篇。

自定义binding实现的确不错,最大的优点就是可以实时改变全局字体

但是也有它的缺点,那就是麻烦,不但要在本地处理一些数据,还要在每个xml中加入binding属性,每个控件都绑定了一个监听,虽然通过livedata进行了优化,但是不可否认的是,它会使内存增大

app运行内存过大,进入后台就很容易被系统回收,体验非常不好。

所以如果从这个角度去考虑的话,系统自带的方法无疑是最合适的选择。

前面提到过的四种方法:

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会自动根据配置的属性去设置字体。

所以,如果产品能接受的前提下(recreate ui,重新加载布局),选择方案1跟方案2无疑是最好的。

方案1的优点,显然就是可以自定义字体大小,固定数值,不过也需要xml中配置style,但是对比自定义binding不会产生大量监听。
方案2的优点,一个字:,缺点也很明显,无法具体自定义字体大小,因为是按缩放处理的。

居然自定义字体大小已经实现过一套方案了,这里就扩展方案2。//(你以为是因为偷懒吗?确实是。)
下面是预览图,太大了加载比较慢,压缩了一下,有点糊。

预览图

男人就是要快,居然要快,那就很简单了,直接在BaseActivity中重写getResources以及attachBaseContext方法。

    @Override
    public Resources getResources() {
        Resources resources = super.getResources();
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            if (resources != null && resources.getConfiguration().fontScale != FontUtils.getAppFontScale()) {
                Configuration configuration = resources.getConfiguration();
                configuration.fontScale = FontUtils.getAppFontScale();
                resources.updateConfiguration(configuration, resources.getDisplayMetrics());
            }
        }
        return resources;
    }

    @Override
    protected void attachBaseContext(Context newBase) {
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N){
            Resources resources = newBase.getResources();
            Configuration configuration = resources.getConfiguration();
            configuration.fontScale = FontUtils.getAppFontScale();
            super.attachBaseContext(newBase.createConfigurationContext(configuration));
        }else {
            super.attachBaseContext(newBase);
        }
    }

高版本系统执行attachBaseContext方法,低版本执行getResources方法。

最后结果都是把我们本地保存的缩放值getAppFontScale设置到系统属性中。

recreate才会生效,所以会存在页面重新加载的情况
FontUtils类非常简单,单纯的记录缩放值,缓存在本地。

public class FontUtils {

    private static final String TAG = "FontUtils";

    private static final String KEY_APP_FONT_STYLE = "key_app_font_style";


    public static final float NORMAL_FONT_SCALE = 1.0f;
    public static final float BIG_FONT_SCALE = 1.2f;
    public static final float LARGE_FONT_SCALE = 1.4f;


    public static float getAppFontScale() {
        return SPUtils.getInstance().getFloat(KEY_APP_FONT_STYLE, NORMAL_FONT_SCALE);
    }

    public static void saveAppFontScale(float appFontScale) {
        SPUtils.getInstance().put(KEY_APP_FONT_STYLE, appFontScale);
    }

}

不过这里也有一个需要注意的地方,就是我们xml中的textSize属性值单位必须为sp,有的可能会设置成dp,虽然显示没影响,但是缩放值fontScale只针对sp才会生效

/**
 * 用户 - 设置字体
 */
public class SettingFontActivity extends BaseActivity {

    public static void startSettingFontActivity() {
        XActivityUtils.startActivity(SettingFontActivity.class);
    }
 
    private float pointScale = 0;

    @Override
    public int getLayoutId() {
        return R.layout.activity_setting_font;
    }

    @Override
    public void initView(Bundle savedInstanceState) {
        mBinding.setClick(new ClickProxy());
        mBinding.includeStatusBar.tvTitle.setText("字体设置");
        pointScale = FontUtils.getAppFontScale();//记录初始值
        loadFontSize(pointScale);
    }

    @Override
    public void onBackPressed() {
        //更改过设置
        if (pointScale != FontUtils.getAppFontScale()) {
            LiveEventBusUtils.postLiveEventBus(LiveEventBusStrKey.FONT_STYLE, new LiveEventBusData.Builder().build());
        }
        super.onBackPressed();
    }

    private void loadFontSize(float scale){
        //单位为dp,默认是sp,会触发缩放效果
        mBinding.tvFontTitle.setTextSize(TypedValue.COMPLEX_UNIT_DIP,18 * scale);
        mBinding.tvFontContent.setTextSize(TypedValue.COMPLEX_UNIT_DIP,16 * scale);
    }

    /**
     * 配置点击事件
     */
    public class ClickProxy {

        public ObservableBoolean isNormalField = new ObservableBoolean(FontUtils.getAppFontScale() == FontUtils.NORMAL_FONT_SCALE);
        public int normalResId = R.mipmap.icon_setting_font_normal_check;
        public int unNormalResId = R.mipmap.icon_setting_font_normal_un;

        public ObservableBoolean isBigField = new ObservableBoolean(FontUtils.getAppFontScale() == FontUtils.BIG_FONT_SCALE);
        public int bigResId = R.mipmap.icon_setting_font_big_check;
        public int unBigResId = R.mipmap.icon_setting_font_big_un;

        public ObservableBoolean isLargeField = new ObservableBoolean(FontUtils.getAppFontScale() == FontUtils.LARGE_FONT_SCALE);
        public int largeResId = R.mipmap.icon_setting_font_large_check;
        public int unLargeResId = R.mipmap.icon_setting_font_large_un;

        //setting
        public void setting(int type){
            switch (type){
                case 1:
                    isNormalField.set(true);
                    isBigField.set(false);
                    isLargeField.set(false);
                    FontUtils.saveAppFontScale(FontUtils.NORMAL_FONT_SCALE);
                    break;
                case 2:
                    isBigField.set(true);
                    isNormalField.set(false);
                    isLargeField.set(false);
                    FontUtils.saveAppFontScale(FontUtils.BIG_FONT_SCALE);
                    break;
                case 3:
                    isLargeField.set(true);
                    isBigField.set(false);
                    isNormalField.set(false);
                    FontUtils.saveAppFontScale(FontUtils.LARGE_FONT_SCALE);
                    break;
            }
            loadFontSize(FontUtils.getAppFontScale());
        }
    }
}

fontScale属性是需要recreate才能生效,如果每次选择都要recreate一下页面,体验太不好了。

这里就需要我们手动去设置textSize,居然是手动设置,就不能用sp为单位了,否则在次回到页面你会发现本来就大号字体又缩放了,变成了超巨大号字体,预览错乱。

代码中可以看到,这边做了一个行为记录,只有在用户改变过字体的时候才会去触发监听,否则每次都更新一遍,recreate一下,显然不是想要的结果。

监听在MainActivity中,这里又有一个注意的点,launchMode设置为singleTop,因为中间存在存活的Activity,所以需要Main来让他们退出栈区,当然,也可以自定义控制,看个人需求。

<activity
            android:name=".ui.main.MainActivity"
            android:configChanges="screenLayout|keyboardHidden|orientation|screenSize|smallestScreenSize"
            android:label="主页"
            android:launchMode="singleTop"
            android:screenOrientation="portrait" />
LiveEventBusUtils.registerLiveEventBus(LiveEventBusStrKey.FONT_STYLE, thisActivity, liveEventBusData -> {
            MainActivity.startMainActivity();//recreate ui
            overridePendingTransition(0, 0);
        });

已知有兼容性问题,说是在app刚好运行时设置系统字体,会影响我们的保存设置,方案是在Application中重写onConfigurationChanged监听,设置了字体会触发监听,重新进入一次。

@Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        //防止app在运行的时候系统设置里更改了字体大小
        if (newConfig.fontScale > 1){
            MainActivity.startMainActivity();
        }
        super.onConfigurationChanged(newConfig);
    }

试过,没复现,但是需要注意。
喜欢就赏个赞吧。

小扩展。

假设有的SplashActivity是在MainActivity中启动的,需要规避一下,因为会导致重新加载SplashActivity,所以在startMainActivity时可以传入一个参数。


不过启动页用fragment来控制会比较好一些,官方也推荐过这种做法。
因为这样就能在加载启动页时开始初始化一些数据,并且不用跳转页面,大大降低了启动时间。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容