Android APP字体随系统字体调整造成界面布局混乱问题解决方案 2020-09-03 IOS开发 多section瀑布流+悬停Header OC shell脚本一键同时推送代码至github和gitee BroadcastReceiver代码分析 不会吧,不会吧,不愧是Ajax,jQuery Ajax啊 微信小程序 使用 fly接口请求框架 面试经常会被问到的节流和防抖,一分钟理解 回流与重绘(浏览器渲染原理) Vue笔记:插槽的使用 初学Qt不会样式表怎么办,打包好的Qt样式表一键生成送给你。 javascript从入门到跑路-----小文的js学习笔记(23)------函数式编程----ForEach、filter、Reduce和Map 聚焦教育行业AI+大数据,「三盟科技」完成B1轮上亿元人民币融资 聚焦教育行业AI+大数据,“三盟科技”完成B1轮上亿元人民币融资 郭宇:28岁“退休程序员” 中信建投智信物联网灵活配置混合C基金最新净值跌幅达1.67% 中信建投智信物联网灵活配置混合A基金最新净值跌幅达1.66% 大数据透视A股复苏:盈利触底反弹 现金流大幅转正 大数据透视A股复苏:盈利触底反弹,现金流大幅转正,产能扩张提速,经营效率显著改善 观赛人数同增12.1%,大数据揭秘中超逆势飘红原因 物联网的银河,华为的桨,少年的歌 大数据显示:长沙互联网人工作满意度、幸福感指数全国第二 日本NHK电视台:贵阳利用大数据产业发展乡村经济和货物运输业 卡奥斯COSMOPlat首发国家质量基础设施物联网平台,打造行业新生态 趣互联:从自助设备SaaS系统+物联网硬件切入线下无人零售市场 苏宁基于 ClickHouse 的大数据全链路监控实践 浪潮下的大数据中心,BAT已从用户变成玩家 威胜信息:能源物联网领航者,竞争力突出、中报逆势增长 新手请进:每个Python程序员都应该知道的10个缩写词 谈谈对于IIc的理解 《信息处理技术》20春期末考试 第十一届蓝桥杯大赛第二次模拟(软件类)真题 +总结(2020年4月,C++描述) Android权限(多个权限申请) virtuoso IC61 教育免费工艺库NCSU 配置问题 浅谈Android MVP openlayers加载Mapbox底图 用大数据为顾客“画像”帮小企业互联网转型 政务上云 服务在线(大数据观察) 大数据“杀熟”?明令禁止!《在线旅游经营服务管理暂行规定》10月起施行 大数据助力福州工程项目提质增速 中国电信:物联网用户突破2亿 Spring Cloud 里 Eureka 什么是微服务及微服务的特点 简历:第一章:技术亮点如何写 记录一个架构师的成长之路【持续更新中...】 MySQL sql语句中变量应用 Unity中加载Texture2D不断消耗内存的问题 mysql-8.0.21安装教程 游戏设计模式——中介者模式(Mediator) 拼多多笔试:最大士兵问题 京东2021校园招聘笔试(8.27编程部分)——数据开发工程师(数列变换A了9%)
您的位置:首页 >大数据 >

Android APP字体随系统字体调整造成界面布局混乱问题解决方案

一、遇到的问题:

当用户调整系统字体大小的时候,APP的字体一般也都会跟随改变,进而导致某些界面布局排版混乱。

下面先说一下关于sp单位的理解

sp单位除了受屏幕密度影响外,还受到用户的字体大小影响,通常情况下,建议使用sp来跟随用户字体大小设置。除非一些特殊的情况,不想跟随系统字体变化的,可以使用dp”。按照这么说,布局宽高固定写死的地方应该统一用dp显示字体,因为一旦用户在设置中调大字体,宽高为固定值的布局显示就乱了。

二、 解决方案:

1.  强制实现所有界面都的字体都不随系统字体大小而改变,在工程的BaseActivity中添加下面的代码。利用Android的Configuration类中的fontScale属性,其默认值为1,会随系统调节字体大小而发生变化,如果我们强制让其等于默认值,就可以实现字体不随调节改变,

 @Overridepublic Resources getResources() {Resources resources = super.getResources();if (resources != null) {Configuration configuration = resources.getConfiguration();if (configuration != null && configuration.fontScale != 1.0f) {configuration.fontScale = 1.0f;//这里只设置字体,故不使用下面注释的方法//configuration.setToDefaults();resources.updateConfiguration(configuration, resources.getDisplayMetrics());}}return resources;}

注意: Android 8.0后在Application中复写上述方法是无效的 (原因暂不清楚,有知道的大佬欢迎指出)。此外,在任意一个Activity中如上覆盖了getResources方法后,会让其它Activity的字体也变的独立于系统配置(这里的Activity只针对重新create的,如当前 Activity 的 fragment,因为没有重新onCreate,就不会重绘进而改变字体)。我的理解是,新的 Activity 会载入上面更新后的 Configuration,而现有的 Activity 则不会更新。所以此方式我只建议用在BaseActivity中实现全部界面字体不随系统更改。

2.  在具体的界面把不想要放大的View字体单位设置为dp

三、原理解析:

到底为什么设置为sp,会导致字体随系统字体大小而改变?

从文字设定大小的入口看,TextView.setTextSize(float size)方法来看:

 /** * Set the default text size to the given value, interpreted as "scaled * pixel" units.This size is adjusted based on the current density and * user font size preference. * * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op. * * @param size The scaled pixel size. * * @attr ref android.R.styleable#TextView_textSize */@android.view.RemotableViewMethodpublic void setTextSize(float size) {setTextSize(TypedValue.COMPLEX_UNIT_SP, size);}/** * Set the default text size to a given unit and value. See {@link * TypedValue} for the possible dimension units. * * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op. * * @param unit The desired dimension unit. * @param size The desired size in the given units. * * @attr ref android.R.styleable#TextView_textSize */public void setTextSize(int unit, float size) {if (!isAutoSizeEnabled()) {setTextSizeInternal(unit, size, true /* shouldRequestLayout */);}}private void setTextSizeInternal(int unit, float size, boolean shouldRequestLayout) {Context c = getContext();Resources r;if (c == null) {r = Resources.getSystem();} else {r = c.getResources();}setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),shouldRequestLayout);}@UnsupportedAppUsageprivate void setRawTextSize(float size, boolean shouldRequestLayout) {if (size != mTextPaint.getTextSize()) {mTextPaint.setTextSize(size);if (shouldRequestLayout && mLayout != null) {// Do not auto-size right after setting the text size.mNeedsAutoSizeText = false;nullLayouts();requestLayout();invalidate();}}}

可以看到,如果没有设置字体单位的时候,默认会分配 TypedValue.COMPLEX_UNIT_SP ,即 sp 单位,而最终的值会通过TypedValue.applyDimension(unit, size, r.getDisplayMetrics()) 方法计算出来赋值给 setRawTextSize 方法,所以接下来看怎么计算的:

/** * Converts an unpacked complex data value holding a dimension to its final floating* point value. The two parameters <var>unit</var> and <var>value</var> * are as in {@link #TYPE_DIMENSION}. * * @param unit The unit to convert from. * @param value The value to apply the unit to. * @param metrics Current display metrics to use in the conversion --*supplies display density and scaling information. ** @return The complex floating point value multiplied by the appropriate* metrics depending on its unit.*/public static float applyDimension(int unit, float value, DisplayMetrics metrics){switch (unit) {case COMPLEX_UNIT_PX:return value;case COMPLEX_UNIT_DIP:return value * metrics.density;case COMPLEX_UNIT_SP:return value * metrics.scaledDensity;case COMPLEX_UNIT_PT:return value * metrics.xdpi * (1.0f/72);case COMPLEX_UNIT_IN:return value * metrics.xdpi;case COMPLEX_UNIT_MM:return value * metrics.xdpi * (1.0f/25.4f);}return 0;}

可以看到,当单位为 COMPLEX_UNIT_SP时,取值为 value * metrics.scaleDensity;所以接下来看 metrics.scaleDensity 的取值:

/** * A scaling factor for fonts displayed on the display.This is the same * as {@link #density}, except that it may be adjusted in smaller * increments at runtime based on a user preference for the font size. */public float scaledDensity;public void setTo(DisplayMetrics o) {if (this == o) {return;}widthPixels = o.widthPixels;heightPixels = o.heightPixels;density = o.density;densityDpi = o.densityDpi;scaledDensity = o.scaledDensity;xdpi = o.xdpi;ydpi = o.ydpi;noncompatWidthPixels = o.noncompatWidthPixels;noncompatHeightPixels = o.noncompatHeightPixels;noncompatDensity = o.noncompatDensity;noncompatDensityDpi = o.noncompatDensityDpi;noncompatScaledDensity = o.noncompatScaledDensity;noncompatXdpi = o.noncompatXdpi;noncompatYdpi = o.noncompatYdpi;}public void setToDefaults() {widthPixels = 0;heightPixels = 0;density =DENSITY_DEVICE / (float) DENSITY_DEFAULT;densityDpi =DENSITY_DEVICE;scaledDensity = density;xdpi = DENSITY_DEVICE;ydpi = DENSITY_DEVICE;noncompatWidthPixels = widthPixels;noncompatHeightPixels = heightPixels;noncompatDensity = density;noncompatDensityDpi = densityDpi;noncompatScaledDensity = scaledDensity;noncompatXdpi = xdpi;noncompatYdpi = ydpi;}

注释说明了,scaleDensity 不仅仅受设备的 density 影响,还受用户设定的字体尺寸影响。DisplayMetrics.scaleDensity 在 DisplayMetrics 类中,并没有初始化的地方,可它是一个 public 的字段,也就是说可以被外部赋值初始化。真正为 DisplayMetrics 中各个字段赋值的地方,在 ResourcesImpl 中,有一个 updateConfiguration() 方法,在其中,就有对 scaleDensity 进行初始化的逻辑。

public void updateConfiguration(Configuration config, DisplayMetrics metrics,CompatibilityInfo compat) {//省略部分代码//...........if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {mMetrics.densityDpi = mConfiguration.densityDpi;mMetrics.density =mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;}// Protect against an unset fontScale.mMetrics.scaledDensity = mMetrics.density *(mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);//省略部分代码//...........}

可以看到,这里又引入了一个新的计算因子,fontScale。而从 Configuration 的源码又了解到,fontScale 默认值为 1 ,这也就是为什么通常情况下,density 和 scaleDensity 的值是相等的,它们分别影响了 dp 和 sp 最终渲染出来的像素尺寸。

所以,我们要控制字体不随系统字体改变的本质,就是通过修改 fontScale 的值为1,这也就是我们方法1这么做的原因。

 

好文分享:当你在设置里修改字体大小的时候,到底在修改什么

Android View篇之 字体大小 调整滑杆的实现

Android 仿微信/支付宝 字体大小 调整控件

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。