我最近完成了我的安卓应用程序开发。我在所有文本大小上使用了sp(缩放像素)。问题是当我调整系统字体大小时,我的应用程序的字体大小也会改变。我可以使用dp(设备独立像素),但这将需要太长时间来维护我的应用程序。
我参考了这里的文本大小。
有没有办法防止系统字体大小的变化影响到我的应用程序?
我最近完成了我的安卓应用程序开发。我在所有文本大小上使用了sp(缩放像素)。问题是当我调整系统字体大小时,我的应用程序的字体大小也会改变。我可以使用dp(设备独立像素),但这将需要太长时间来维护我的应用程序。
我参考了这里的文本大小。
有没有办法防止系统字体大小的变化影响到我的应用程序?
如果您希望文本保持相同的大小,您将需要使用 dp
。
引用 文档:
sp
是基于相同单位的,但是会根据用户偏好的文本大小进行缩放的(它是一个缩放无关的像素),因此定义文本大小时应使用此测量单位(但永远不要用于布局大小)。
我强调了一下。
因此,如果您将 sp
作为文本大小的单位,您将看到预期的行为。
我不明白您所说的使用 dp 需要太长时间来维护应用程序 - 据我所知,这将需要完全相同的努力?(也许少一些,但可能会使视力差的用户难以使用)
之前的答案都没能在我的Android 8.1 (API 27)上生效。这是可行的解决方案:将以下代码添加到您的Activity中:
Kotlin代码:
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
。
super.attachBaseContext(newBase);
需要移动到attachBaseContext()的最后一行。 - Robertoverride.setToDefaults();
来确保每个设备的默认设置将被重置,而不必担心每个设备的不同设置。 - Eiri我最近也遇到了这个问题。我们的用户界面在屏幕尺寸有限的手机上不能很好地缩放,而为了应对用户设置他们的辅助选项为“巨大”时的情况,更改整个用户界面似乎是不明智的。
我发现这篇 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.30f以上(1.30f在Note 5上是“大号”,但在不同设备上可能会略有不同)。如果用户将字体放得过大(“特大号”,“巨型号”...),我们只将应用程序缩放到“大号”。
这使您的应用程序能够根据用户偏好进行一定程度的缩放,而不会扭曲您的UI。希望这能帮助其他人。祝缩放顺利!
其他提示
如果您希望某些布局随字体大小一起缩放(例如...您用作与字体背景相对的RelativeLayout
),则可以使用sp而不是经典的dp来设置其宽度/高度。当用户更改字体大小时,布局将相应地随着应用程序中的字体而变化。这是一个很好的小技巧。
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
。我是Android新手,正在尝试选择“最佳”答案。谢谢! - Tallboy这就是我们的做法。 在类中像这样覆盖。 如果您希望为不同的活动获取不同的行为-在Activity中覆盖。
不要忘记添加清单标签android: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);
}
在一些辅助类中,我们有一个adjustFontScale()方法。
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);
}
}
警告!这将完全忽略辅助功能字体比例设置,防止您的应用程序字体缩放!
updateConfiguration
的源代码实现(在 5.1 和 10 中),手动更新 scaledDensity
是不必要的。它们已经为您完成了。 - Peter这是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);
}
}
你只需要重写活动的 attachBaseContext 方法并在那里更新配置即可。
getBaseContext()。getResources()。updateConfiguration()虽然有许多使用此方法的示例,但已被弃用。如果您使用此方法,除了IDE警告之外,您可能会发现应用程序的某些部分没有按比例缩放。
还有一种方法可以防止应用程序布局问题/字体问题在设置字体大小更改时出现。你可以尝试
// ignore the font scale here
final Configuration newConfiguration = new Configuration(
newBase.getResources().getConfiguration()
);
newConfiguration.fontScale = 1.0f;
applyOverrideConfiguration(newConfiguration);
其中newBase来自于attachBaseContext函数。你需要在你的Activity中覆盖这个回调函数。
但是,副作用是,如果你想要使用动画(objectanimator/valueanimator),那么它会导致奇怪的行为。
我尝试过在整个应用中禁用fontScale
,虽然它能起到效果,但是我认为这是一个糟糕的想法,因为只有一个原因:
你并没有为视力障碍者提供更好的app体验。
我认为更好的方式是可以允许字体缩放,但只限于一些地方,在这些地方,你无法调整文本大小使其易读。
实现 (10.02.22)
经过一天的思考后,我创建了一个TextView
的Kotlin扩展(也可以用于EditText
,因为它是子类):
fun TextView.removeFontScale() {
val fontScale = resources.configuration.fontScale
if (fontScale != 1f) {
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale / fontScale
textSize = newTextSize
}
}
scaledTextSize
分成两部分,因为在设置TextView
的新textSize
后,字体缩放会发生。
更新/修复(14.02.22)
以前的解决方案在真实设备上不起作用,只在模拟器上起作用(反之亦然增加文本大小)。所以,我找到了另一种方法:
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale
setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSize)
TextView
的新的 textSize
不会根据 fontScale
值进行缩放。
使用示例:
titleText.removeFontScale()
subtitleText.removeFontScale()
工作日志,大小以1.3/1.5比例缩小后就和1.0比例一样了:
Old realization (10.02.22):
fontScale: 1.0 | oldSize: 20
fontScale: 1.3 | oldSize: 26 | newSize: 20.0
New realization (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
顺便说一句,我注意到标准的 工具栏
没有缩放字体,后来我明白这种做法是可以的(在真正需要禁用缩放的地方)。
我遇到了相同的问题,并通过更改XML文件中的 sp
为 dp
来解决它。然而,我还需要调整 WebViews 的文本大小。
通常情况下,要调整 WebView
的文本大小,使用 setDefaultFontSize()
函数。但是,它的默认值单位是 sp
。
在我的项目中,我使用 setTextZoom(100)
来修正 WebView 的文本和图标大小。*(stackoverflow 上有其他方法,但几乎全部废弃不用)*
WebSettings settings = mWebView.getSettings();
settings.setTextZoom(100);
您可以使用基础活动配置强制调整应用程序的文本大小,使所有活动都继承基础活动。将1.0f设置为字体大小将忽略系统设置并强制应用程序的字体大小为正常大小。
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);
}
我认为最接近的最佳答案来自以下用户:“如果您有一个可用的应用程序,并且将所有 sp 值切换为 dp 值很容易出错,需要更多时间来完成。然后,如果选择再次切换以支持它,您需要恢复所有这些更改。覆盖父活动中的 fontScale 才更合理。这比更改资源所需的工作量要小。这也更易于维护。在下面的回答中,覆盖 Context 是正确的方法。” - parohy" 2021年4月7日 5:18
dp
并忽略lint错误!这里有一篇关于如何禁用lint错误的博客文章,可能会有用。 - Adam Ssp
值切换为dp
值是容易出错并需要更多时间才能完成。如果选择再次切换以支持它,您需要撤消所有这些更改。覆盖父活动中的fontScale
比更改资源需要更少的工作量。这也更易于维护。在下面回答,覆盖Context
是正确的方法。 - parohy