如何防止系统字体大小的变化影响到 Android 应用程序?

我最近完成了我的 Android 应用程序的开发。我对所有 textSize 使用 sp (缩放像素)。问题是,当我调整系统的字体大小时,我的应用程序的字体大小正在改变。我可以使用 dp (与设备无关的像素) ,但是维护我的应用程序需要很长时间。

我引用了 这个的文本大小。

是否有办法防止系统字体大小的变化影响我的应用程序?

78586 次浏览

如果要求文本保持相同大小,则必须使用 dp

引用 文件的话:

sp是相同的基本单位,但是按照 用户首选的文本大小缩放(它是一个与尺寸无关的像素) ,所以在定义文本大小时应该使用这个测量单位(但是决不能用于布局大小)。

重点是我的。

因此,您可以看到使用 sp作为文本大小单位的预期行为。

我不明白你说使用 dp 维护你的应用程序花费太长时间是什么意思——据我所知,这将是完全相同的工作量?(也许更少,尽管它可能会使视力不好的用户更难使用)

我最近也遇到了这个问题。在屏幕尺寸有限的手机上,我们的用户界面没有扩展得很好,并且在用户设置的可访问性选项为“巨大”的可能性微乎其微的情况下改变了整个用户界面,这看起来很愚蠢。

我发现这个关于 StackOverflow 的 有个问题非常有用。

我所做的就是将下面的代码放在我的 BaseActivity (一个我所有活动都从中扩展的 Activity 类)中

public void adjustFontScale(Configuration configuration) {
if (configuration.fontScale > 1.30) {
LogUtil.log(LogUtil.WARN, TAG, "fontScale=" + configuration.fontScale); //Custom Log class, you can use Log.w
LogUtil.log(LogUtil.WARN, TAG, "font too big. scale down..."); //Custom Log class, you can use Log.w
configuration.fontScale = 1.30f;
DisplayMetrics metrics = getResources().getDisplayMetrics();
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.fontScale * metrics.density;
getBaseContext().getResources().updateConfiguration(configuration, metrics);
}
}

然后在我的 super.onCreate()之后这样叫它

adjustFontScale(getResources().getConfiguration());

这段代码的作用是识别用户是否在可访问性设置中将字体大小设置为大于1.30 f (1.30 f 在 Note 5中是“大”,但可能因设备而有所不同)。如果用户设置他们的字体太大(“特大”,“巨大”...) ,我们的应用程序规模只有“大”。

这使得你的应用程序可以根据用户的喜好(在一定程度上)进行扩展,而不会扭曲你的用户界面。希望这能帮到其他人。祝你好运!

其他贴士

如果您希望某些布局与您的字体缩放(例如... RelativeLayout,您使用作为背景对您的字体) ,您可以设置它们的宽度/高度与 sp,而不是经典的 dp。当用户更改字体大小时,布局将随应用程序中的字体相应更改。不错的小把戏。

我们就是这么做的。 在 申请类中像这样覆盖 < em > onConfigurationChanged () 。 如果您希望不同的活动有不同的行为-在活动中覆盖 < em > onConfigurationChanged ()

不要忘记添加清单标记 ConfigChanges = “ fontScale”,因为这个配置更改是您自己处理的。

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);


// In some cases modifying newConfig leads to unexpected behavior,
// so it's better to edit new instance.
Configuration configuration = new Configuration(newConfig);
SystemUtils.adjustFontScale(getApplicationContext(), configuration);
}

在一些助手类中,我们有 调整字表() 方法。

public static void adjustFontScale(Context context, Configuration configuration) {
if (configuration.fontScale != 1) {
configuration.fontScale = 1;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.fontScale * metrics.density;
context.getResources().updateConfiguration(configuration, metrics);
}
}

警告! 这将完全忽略辅助性字体缩放用户设置,并将阻止您的应用程序字体缩放!

还有另一种方法可以防止应用程序布局问题/字体问题从设置字体大小变化。 你可以试试

// ignore the font scale here
final Configuration newConfiguration = new Configuration(
newBase.getResources().getConfiguration()
);


newConfiguration.fontScale = 1.0f;
applyOverrideConfiguration(newConfiguration);

其中 newBase 来自 attachBaseContext 函数。 您需要在活动中重写此回调。

但是,副作用是,如果您想使用动画(objectanimator/valueanimator) ,那么它将导致奇怪的行为。

这就是你在2018年的做法(Xamarin. Android/C #-其他语言的同样方法) :

public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
...
}


protected override void AttachBaseContext(Context @base)
{
var configuration = new Configuration(@base.Resources.Configuration);


configuration.FontScale = 1f;
var config =  Application.Context.CreateConfigurationContext(configuration);


base.AttachBaseContext(config);
}
}

您只需覆盖活动的 附件 BaseContext方法并在那里更新配置。

GetBaseContext ().GetResources ().UpdateConfiguration () 是不推荐的,尽管这种方法有许多示例。如果除了 IDE 警告之外还使用这种方法,您可能会发现应用程序的某些部分没有缩放。

在 Android 8.1(API 27)上,以前的答案对我来说都不管用。下面是有效的方法: 在你的活动中添加以下代码:

科特林代码:

override fun attachBaseContext(newBase: Context?) {
    

val newOverride = Configuration(newBase?.resources?.configuration)
newOverride.fontScale = 1.0f
applyOverrideConfiguration(newOverride)


super.attachBaseContext(newBase)
}

Java 代码:

@Override
protected void attachBaseContext(Context newBase) {
    

final Configuration override = new Configuration(newBase.getResources().getConfiguration());
override.fontScale = 1.0f;
applyOverrideConfiguration(override);


super.attachBaseContext(newBase);
}

你不需要改变你的 AndroidManifest.xml

你可以强制文本大小的应用程序使用基础活动配置,使所有活动固有的基础活动。1.0 f 将强制应用程序的字体大小正常忽略系统设置。

public  void adjustFontScale( Configuration configuration,float scale) {


configuration.fontScale = scale;
DisplayMetrics metrics = getResources().getDisplayMetrics();
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.fontScale * metrics.density;
getBaseContext().getResources().updateConfiguration(configuration, metrics);


}


@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adjustFontScale( getResources().getConfiguration(),1.0f);
}

我认为使用 dp是最好的方式,但在某些情况下,您可能想使用字体样式。但是,风格是使用 sp,你可以通过以下方法将 sp转换成 dp:

fun TextView.getSizeInSp() = textSize / context.resources.displayMetrics.scaleDensity


fun TextView.convertToDpSize() = setTextSize(TypedValue.COMPLEX_UNIT_DIP, getSizeInSp())

因此,您可以使用 sp值从样式没有动态字体大小,也不需要硬编码的字体大小

我遇到了同样的问题,通过在。XML 文件。但是,我还需要修复 网上浏览的文本大小。

通常,要调整 WebView的文本大小,需要使用 setDefaultFontSize()函数。但是,它的默认值单元是 sp

在我的项目中,我使用 setTextZoom(100)来修复 WebView 的文本和图标大小。* (stackoverflow 中还有其他方法,但几乎都是 不赞成) *

WebSettings settings = mWebView.getSettings();
settings.setTextZoom(100);

有关 setTextZoom ()的详细信息,请参阅

尝试了在整个应用程序中禁用 fontScale的解决方案。这是工作,但我来回答这是一个 糟糕的主意只有一个原因:

你没有让你的应用程序更适合视障人士。

更好的方法(我认为)是允许字体缩放,但只在某些地方有限制,在那里你不能缩放你的文本,因为它看起来可读。


实现 (10.02.22)

经过一天的思考,我为 TextView创建了 科特林扩展(也可以用于 EditText,因为它是一个孩子) :

fun TextView.removeFontScale() {
val fontScale = resources.configuration.fontScale
if (fontScale != 1f) {
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale / fontScale
textSize = newTextSize
}
}

需要除 scaledTextSize两次,因为后设置一个新的 textSizeTextView字体比例将发生。


更新/FIX (14.02.22)

以前的解决方案不适用于实际设备,只适用于模拟器(反之亦然,它会过度增加文本大小)。所以,我找到了另一种方法:

val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale
setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSize)

这里新的 textSizeTextView设置后,将不会在 fontScale值上缩放。


举例说明:

titleText.removeFontScale()
subtitleText.removeFontScale()

日志 的工作,大小在 1.3/1.5规模成为像在 1.0规模:

  • 旧的认识(10.02.22) :

    fontScale: 1.0 | oldSize: 20
    fontScale: 1.3 | oldSize: 26 | newSize: 20.0
    
  • 新的认识(14.02.22) :

    fontScale: 1.5 | oldSize: 83.0 | newSize: 55.333332 | textSize: 55.333332
    fontScale: 1.5 | oldSize: 58.0 | newSize: 38.666668 | textSize: 38.666668
    

附注: 我注意到标准的 Toolbar没有缩放字体,之后我明白这种做法是可以的(禁止缩放它真正需要的地方)。