还是那个问题,搬砖遇到什么,就学什么。 今天搬砖过程中被提了一个要求,APP不应该随着系统的字体大小变化而变化。emmm?机智的我第一时间就想到了大名鼎鼎的屏幕适配方案AndroidAutoSize,但是这个砖有一个特点,就是宽度是固定设计的,高度不是,而且屏幕适配方案根本就没有,OK,这只是一个砖而已。通过导入maven吧,感觉也可以解决这个问题,但是这个明显是没有要求屏幕适配,鬼知道屏幕适配方案整上去了,UI会不会鬼一样,这个不敢保证,OK,那就破罐子破摔,我们自己搞。
正文
还是不忘初心,再次回顾一下,我们想要的只是sp定义的字号不被系统影响。嗯,贼单纯,屏幕适配?什么锅太大了,听不清。
dp和sp的使用上的差异
经常用dp当成字号的同学可能有经验,毕竟盛传dp做字号是不会被系统字号所影响的。至于为啥,我们直接剽窃一波大模型:
在Android中,dp和sp都是用来设置字体大小的单位,但它们之间存在一些重要的区别。 dp(dp Clip)是密度无关像素,它只与屏幕的像素密度有关。这意味着无论在什么设备或分辨率的屏幕上,1dp代表的物理尺寸都是相同的。然而,sp(scaled pixel)则不同,它会根据用户的字体大小偏好和屏幕密度进行缩放。 具体来说,如果用户在Android设备上将字体大小设置为“正常”,那么1sp和1dp将相等,都等于0.00625英寸。
但当用户将字体大小设置为“大”或“超大”时,1sp的值将大于1dp。
因此,使用dp还是sp取决于你的具体需求。如果你希望字体大小与屏幕密度无关,那么应该使用dp。而如果你希望字体大小能够根据用户的偏好进行缩放,那么应该使用sp。
ok,懂了,以后全用dp写字号。通过上面的信息我们可以大致的知道sp是会随着系统而变化的,至于咋变换的,后期再说。
autoSize 怎么做到使用SP但是可以不缩放的
强烈推荐读大佬的原文好吧,《骚年你的屏幕适配方式该升级了!-今日头条适配方案》 翻完大佬的blog,道理都懂,什么density是呢?
Android中的 density(密度)表示每英寸有多少个显示点(逻辑值),其单位是dpi,即dot per inch。
一般来说,屏幕实际分辨率为240px400px时,density=120;屏幕实际分辨率为320px533px时,density=160;屏幕实际分辨率为480px*800px时,density=240。需要注意的是,这些密度值是基于一个160dpi的屏幕计算的,因此在不同设备或分辨率的屏幕上,1dp或1px代表的物理尺寸可能会有所不同。
这和我想要不缩放字号有什么关系,除非我用了dp,但是好像也没啥关系。通过之前的经验我们知道这个组件支持设置app不随着系统字体变化的功能: AutoSizeConfig.getInstance().setExcludeFontScale(true); 我们直接查看代码的调用位置不就找到了大佬如何处理这个问题的方式了吗?
/**
* 是否屏蔽系统字体大小对 AndroidAutoSize 的影响, 如果为 {@code true}, App 内的字体的大小将不会跟随系统设置中字体大小的改变
* 如果为 {@code false}, 则会跟随系统设置中字体大小的改变, 默认为 {@code false}
*/
private boolean isExcludeFontScale;
/**
* 区别于系统字体大小的放大比例, AndroidAutoSize 允许 APP 内部可以独立于系统字体大小之外,独自拥有全局调节 APP 字体大小的能力
* 当然, 在 APP 内您必须使用 sp 来作为字体的单位, 否则此功能无效, 将此值设为 0 则取消此功能
*/
private float privateFontScale;
看到,大佬竟然体提供了两套方式,privateFontScale这个明显可以用来做老年版app嘛,超大字体。继续查看组件代码可以看到都是在对于 DisplayMetrics 这个对象进行赋值。DisplayMetrics 官网,我们再结合官方文档,可以找到一个变量:scaledDensity
哈,竟然34就过期了,得快去提issues,这个方案快不支持了。
可以看到代码中,当isExcludeFontScale=true的时候,targetScaledDensity和targetDensity是相等的,这两个值分别对应着:
displayMetrics.scaledDensity和displayMetrics.density。
那么结合大佬的思路,可以看到,如果说单纯的设置字体,这个方案有点恼火,该有的计算还是得有。
基于 Configuration 呢
于是我们换一个思路,既然都是基于resources,那么resources有没有提供其他的配置类呢?那就是:Configuration。 Configuration 官方文档
此类描述了可能影响应用程序检索的资源的所有设备配置信息。这包括用户指定的配置选项(区域设置列表和缩放)以及设备配置(例如输入模式、屏幕尺寸和屏幕方向)。
Resources
您可以使用从 获取此对象Resources.getConfiguration()
。因此,从活动中,您可以通过将请求链接到以下方式来获取它ContextThemeWrapper.getResources()
:
Configuration config = getResources().getConfiguration();
我们直接看对应部分的变量:
public int | densityDpi 渲染的目标屏幕密度,对应于 密度 资源限定符。 |
---|---|
public float | fontScale 当前用户对字体缩放因子的偏好(相对于基本密度缩放)。 |
public int | fontWeightAdjustment 调整文本字体粗细。 |
ok ,fontScale就很明显的适合我们。那就就开整:我们打印了下这个值的默认值是1.0f。ok,那么我们判断它只要不是1.0f就给它设置为1.0f不就好了。
override fun getResources(): Resources {
val resources = super.getResources()
val configuration = resources.configuration
if (configuration.fontScale != 1.0f) {
LogUtils.e(configuration.fontScale)
configuration.fontScale = 1.0f
// 但是这个函数被标记为过期了
//resources.updateConfiguration(configuration, resources.displayMetrics)
// 那么我们直接这么来
return createConfigurationContext(configuration).resources
}
return resources
}
如果扩展到所有activity,那就写成base 或者拿到application所在的Resources对象进行设置不就好了。
总结
主要还是对两个Configuration和DisplayMetrics提供了的东西进行一些认知层面的上的扩展。逻辑还是很简单的。了解这个之后,我们做APP的老年版本逻辑层面上就懂了好吧,主要还是改这两个类,至于选择那个?还是看思路。
当然还有一些糊弄过去的地方,比如说context不同下的resources 的优先级没有搞明白等等。 OK,水完了,溜了,溜了,起码今天没有卡12点。