在做混合开发时发现,无论是APP内的字体大小,还是前端的字体大小,都会随着系统字体大小发生变化。当遇到老人字体(特大号字体)时,有些页面的布局就乱掉了。而玩过游戏的都知道,所有游戏APP的字体都不会随着系统的字体变化而变化。

有两种思路:

  1. 利用 dip(device independent pixels,设备独立像素)作为字体单位。这样的话,一个是在所有手机上字体看起来都差不多大,而且也不随系统字体变化而变化。Unity做出来的游戏就是用这种方案。缺点也很明显,就是都在xml里写死了,无法修改。
  2. 重写 getResources() 方法,将 fontScale 写死为 1,来避免缩放。优点是,可以在不重启应用的情况下,随时修改字体大小。

下面主要介绍下第2种方案

Context 实际上是一个抽象类,它由 ContextWrapper 来代理。从源码得知,mBase 仅能在构造函数 或 attachBaseContext 函数中被赋值。

Android 的顶级组件 Application、Activity、Service 继承自 ContextWrapper。这类组件的生命周期其实是交给 Android 系统来托管的。因此在创建时并不能立马确定上下文,直到被调用 attachBaseContext 时,才被赋予上下文。

在 Activity 中创建的控件,在其访问资源时,会自动调用 getResources() 去确定获取资源的路径,同时获取资源。

如果只是要改变 Activity 的字体,仅重写 getResources()

因此,要想不依赖系统的字体大小,我们就可以创建一个 BaseActivity.java,然后所有 Activity 都作为它的子类

public class BaseActivity extends FragmentActivity {static float fontScale = 1f;@Overridepublic Resources getResources() {Resources resources = super.getResources();Configuration config = resources.getConfiguration();if(config.fontScale != fontScale) {config.fontScale = fontScale;return context.createConfigurationContext(config).getResources();} else {return resources;}}
}

updateConfiguration 方法已经被 deprecated 了,google 推荐我们使用 createConfigurationContext 方法来更新配置。

这样Activity的字体,就可以独立于系统了。但是发现一个问题,Fragment 中的字体还是跟随系统变化。

查看 fragment 源码,发现 getResources 被声明为 final,也就是不可被重写。

于是我们就只能在 fragment 的 context 被赋值时,也就是 onAttach 方法修改字体放大比例了。而传给 Fragment 函数 onAttach 的 context 源自于 Activity,因此,我们只需要在重写 Activity 的 attachBaseContext 方法。

注意:无论字体缩放比例是否相同,这里一定要重新调用 createConfigurationContext 创建新的 Context,否则虽然打印出的 fontScale 和我们设定的一致,但依然用的是系统的 fontScale

错误写法

 @Overrideprotected void attachBaseContext(Context base) {Log.i(TAG, "attachBaseContext");Configuration config = base.getResources().getConfiguration();if(config.fontScale != fontScale) {config.fontScale = fontScale;Context context = context.createConfigurationContext(config);super.attachBaseContext(context);} else {super.attachBaseContext(base);}}

正确写法

 @Overrideprotected void attachBaseContext(Context base) {Log.i(TAG, "attachBaseContext");Configuration config = base.getResources().getConfiguration();config.fontScale = fontScale;Context context = context.createConfigurationContext(config);super.attachBaseContext(context );}

接下来,我们要仿微信那样,动态修改字体大小。

第一步那便是刷新 Activity,让 getResources 方法被重新调用,有如下几个可选方案(最后发现只有一个可行)

  1. activity.getWindow().getDecorView().invalidate()
  2. activity.getWindow().getDecorView().requestLayout()
  3. activity.recreate()

recreate 之后会立马触发 attachBaseContext 绑定 context,然后重新调用 getResources 重新获取资源。

利用这一点,我们就可以补全 BaseActivity 里面的方法,使之动态改变字体大小了。

如果仅需要改变 activity 的字体,仅需要重写 getResources 即可,但如果还需要动态修改

将公共方法提取到一个公用类里面,DisplayUtil.java

public class DisplayUtil {/*** 保持字体大小不随系统设置变化(用在界面加载之前)* 要重写Activity的attachBaseContext()*/public static Context attachBaseContext(Context context, float fontScale) {Configuration config = context.getResources().getConfiguration();Log.i(TAG, "changeActivityFontScaleA " + config.fontScale + ", " + fontScale);//错误写法
//        if(config.fontScale != fontScale) {
//            config.fontScale = fontScale;
//            return context.createConfigurationContext(config);
//        } else {
//            return context;
//        }//正确写法config.fontScale = fontScale;return context.createConfigurationContext(config);}/*** 保持字体大小不随系统设置变化(用在界面加载之前)* 要重写Activity的getResources()*/public static Resources getResources(Context context, Resources resources, float fontScale) {Configuration config = resources.getConfiguration();Log.i(TAG, "changeActivityFontScaleR " + config.fontScale + ", " + fontScale);if(config.fontScale != fontScale) {config.fontScale = fontScale;return context.createConfigurationContext(config).getResources();} else {return resources;}}/*** 保存字体大小,后通知界面重建,它会触发attachBaseContext,来改变字号*/public static void recreate(Activity activity) {
//          activity.getWindow().getDecorView().requestLayout();
//          activity.getWindow().getDecorView().invalidate();//只有这句才有效,其它两句都无效activity.recreate();}/*** 保存字体大小,后通知界面重建,它会触发attachBaseContext,来改变字号*/public static void recreate(Activity activity) {
//          activity.getWindow().getDecorView().requestLayout();
//          activity.getWindow().getDecorView().invalidate();//只有这句才有效,其它两句都无效activity.recreate();}
}

BaseActivity.java

public class BaseActivity extends FragmentActivity {private static final String TAG = "BaseActivity";static float fontScale = 1f;@Overridepublic Resources getResources() {Log.i(TAG, "getResources");Resources resources = super.getResources();return DisplayUtil.getResources(this, resources, fontScale);}@Overrideprotected void attachBaseContext(Context base) {Log.i(TAG, "getResources");super.attachBaseContext(DisplayUtil.attachBaseContext(base, fontScale));}/*** 设置字体大小,同时通知界面重绘*/public void setFontScale(float fontScale) {Log.i(TAG, "setFontSize " + fontScale);this.fontScale = fontScale;DisplayUtil.recreate(this);}
}

测试代码节选

public interface DataCallback1<T> {void onData(T data);
}/*** 显示拖动条对话框*/
public static AlertDialog showSeekBar(Context context, @StringRes int titleId, int max, int progress, DataCallback1<Integer> callback) {SeekBar seekBar = new SeekBar(context);seekBar.setMax(max);seekBar.setProgress(progress);int padding = 80;seekBar.setPadding(padding, padding, padding, 0);AlertDialog dialog = new AlertDialog.Builder(context).setTitle(titleId).setView(seekBar).setPositiveButton(R.string.ok, (dialog1, which) -> callback.onData(seekBar.getProgress())).setNegativeButton(R.string.cancel, null).create();dialog.show();return dialog;
}private void resizeFont() {AlertDialog dialog = DialogUtil.showSeekBar(activity, data, 5, 0, data14 -> {float value = 0.5f + 0.5f * data14;activity.setfontScale(value);});
}

main_fragment.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" ><TextViewandroid:id="@+id/message"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="MainFragment 默认" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="12sp"android:text="MainFragment 12sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20sp"android:text="MainFragment 20sp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="12dp"android:text="MainFragment 12dp" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:textSize="20dp"android:text="MainFragment 20dp" /></LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

设置字体大小方法:设置 =》显示与亮度 =》字体大小


使用测试程序进行测试

经过测试可以发现,TextView 的默认字体大小是15,而且单位是 sp。
sp 的字体会随着系统字体大小( 或者 Configuration.fontScale )而缩放,而 dp 是设备无关像素单位,不管系统字体怎么修改,它都会保持一致,不会改变。

TextView 的默认字体大小是15,单位是sp,这点也可以从 TextView 的源码中获取佐证。

Android App修改字体大小,且不随系统字体大小更改相关推荐

  1. Android APP修改全局字体

    字体下载网址:自定义字体.7z 1 复制工具类 package com.lym.uifont.utils;import android.app.Activity; import android.con ...

  2. android 禁止跟随系统字体,Android 应用全局字体调节或禁止随系统字体大小更改...

    1.禁止跟随系统字体大小调节 在Application中复写getResources()方法 @Override public Resources getResources() {//还原字体大小 R ...

  3. 如何设置app字体跟随系统_Android 应用全局字体调节或禁止随系统字体大小更改...

    禁止跟随系统字体大小调节 如果要像微信一样,所有字体都不允许随系统调节而发生大小变化,要怎么办呢?利用Android的Configuration类中的fontScale属性,其默认值为1,会随系统调节 ...

  4. 安卓APP的字体大小设置不受系统字体大小的影响

    一,前言 近期在优化代码的过程中 发现这么一个问题 测试机有一个小屏的手机 展示的布局文件比较拥挤,而且字体较大.为了优化这一个问题 刚开始以为是字体适配的问题 后来无意中发现在手机的显示设置 -字体 ...

  5. 设置APP字体大小不跟随手机系统字体而改变

    设置H5页面中字体大小不跟随手机系统 WebSettings webSettings = webView.getSettings();webSettings.setTextZoom(100); 设置原 ...

  6. Flutter Android app 修改启动背景颜色和logo——筑梦之路

    打开android\app\src\main\res\drawable\launch_background.xml, <?xml version="1.0" encoding ...

  7. android 怎么获取app 字体颜色,android app 修改字体

    android中可能会遇到修改字体的情况,虽然说需求比较少,但是偶尔还会遇到 可以使用三方框架来帮助我们简单做到 api "uk.co.chrisjenx:calligraphy:2.2.0 ...

  8. Android app中调用启动其他应用(系统应用和第三方应用

    一.打开第三方应用 方法一 Intent intent=new Intent(); //包名 包名+类名(全路径) intent.setClassName("com.linxcool&quo ...

  9. android改变整个app字体大小,Android系统字体大小如何影响app的字体大小?

    在Android应用开发过程中,一定会碰到本来完美的布局,在系统字体大小设置[最大]时变成一团浆糊.解决办法网上也有很多,但是分析原理的却几乎没看到.博主在碰到问题的第一时间也是直接用了网上的方法,即 ...

最新文章

  1. spring18-4: spring aop
  2. python中key的意思_有朋友问Python 中实例对象为啥能按照key赋值。
  3. Python+Selenium+PIL+Tesseract真正自动识别验证码进行一键登录
  4. SAP License:SAP信用控制
  5. 【渝粤教育】电大中专建筑力学_1作业 题库
  6. bodymovin导出没有html5,AE脚本-导出json格式的Web动画工具 Bodymovin v5.7.1 + 使用教程...
  7. Raid5磁盘阵列数据恢复,服务器raid数据恢复步骤和方法
  8. 远程接入Linux、unix、Windows工具-opentext ETX
  9. angularjs实现 - 增删改查+排序+敏感字(最终版)
  10. rstp 小米网络摄像头_小蚁摄像头实时同步视频到群晖 nas(2)—— 使用 rtsp 协议同步...
  11. 转 研发团队绩效评定机制细则。
  12. edm邮件直投_EDM邮件直投专家 :直接投递Email到收件人邮箱 Version 4.0.9 Build 503
  13. 【AviUtl】动画效果,简易Glitch++(派生),学习笔记
  14. 3D目标检测-BEVFormer、BEVDepth
  15. 微信小程序 使用 uCharts 图表
  16. 手机端宝贝描述中每张图片的宽要在480到1500之间,最大高度为2500, 以下图片不满足
  17. 内马尔赛后发飙内马尔赛后发飙
  18. ubuntu 部署STF
  19. HX/VSAN超融合技术学习笔记
  20. 模拟163邮箱登录钓鱼PHP源码,分享一个curl模拟网易163邮箱登录实例

热门文章

  1. web前端|品优购|html+css|代码
  2. 空间规划中的“以流定形”:空间关系-空间活动-空间网络
  3. BIGEMAPapp导入文件方式
  4. 春不语,春天却能催醒百花。
  5. mysql查询学生表的总人数,MySQL查询练习2
  6. TrackingMore API C# 调用示例
  7. DFS + BFS + 洛谷题
  8. Corral the Cows(二分、前缀和、离散化)
  9. Android 面试文档分享
  10. 解析SCUT FIR Pedestrian Dataset数据