【发布时间】:2014-02-28 02:05:13
【问题描述】:
我最近完成了我的 android 应用程序的开发。我对所有 textSize 都使用了 sp(缩放像素)。问题是当我调整系统字体大小时,我的应用程序的字体大小正在改变。我可以使用 dp(与设备无关的像素),但维护我的应用程序需要很长时间。
我从 this 引用了文本大小。
有没有办法防止系统字体大小更改对我的应用程序产生影响?
【问题讨论】:
标签: android android-fonts
我最近完成了我的 android 应用程序的开发。我对所有 textSize 都使用了 sp(缩放像素)。问题是当我调整系统字体大小时,我的应用程序的字体大小正在改变。我可以使用 dp(与设备无关的像素),但维护我的应用程序需要很长时间。
我从 this 引用了文本大小。
有没有办法防止系统字体大小更改对我的应用程序产生影响?
【问题讨论】:
标签: android android-fonts
如果您要求文本大小保持不变,则必须使用dp。
sp是相同的基本单位,但按用户首选的文本大小(它是与比例无关的像素)进行缩放,因此在定义文本大小时应使用此测量单位 (但从不用于布局尺寸)。
强调我的。
因此,您看到了使用 sp 作为文本大小单位的预期行为。
我不明白您所说的使用 dp 来维护您的应用程序的时间太长是什么意思 - 据我所知,这将是完全相同的工作量吗? (也许更少,尽管它可能会使其对视力不佳的用户不太有用)
【讨论】:
dp 并忽略 lint 错误!有一个 blog post on how to disable lint errors 可能有用。
sp 值切换为 dp 值很容易出错,并且需要更多时间来执行此操作。然后,如果选择切换回来再次支持它,您需要恢复所有这些更改。在父活动中覆盖fontScale 更有意义。与更改资源相比,这需要更少的努力。它的可维护性也更好。下面的答案覆盖Context 是要走的路。
我最近也遇到了这个问题。我们的 UI 在屏幕尺寸有限的手机上无法很好地扩展,并且如果用户将其辅助功能选项设置为“巨大”似乎很愚蠢,就更改了整个 UI。
我发现 StackOverflow 上的 question 最有帮助。
我所做的是将以下代码放入我的 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 上是“大”,但可能因设备而异)。如果用户将他们的字体设置得太大(“Extra Large”、“Huge”...),我们只会将应用程序缩放为“Large”。
这允许您的应用在不扭曲 UI 的情况下(在一定程度上)根据用户的偏好进行扩展。希望这对其他人有帮助。祝你好运!
其他提示
如果您希望某些布局与您的字体一起缩放(例如...RelativeLayout,您将其用作字体的背景),您可以使用 sp 而不是经典的 dp 设置它们的宽度/高度。当用户更改其字体大小时,布局将随着应用程序中的字体而相应更改。不错的小技巧。
【讨论】:
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);。我是 android 新手,正在尝试选择“最佳”答案。谢谢!
我们就是这样做的。 在 Application 类中重写 onConfigurationChanged() 像这样。 如果您希望不同活动的不同行为 - 覆盖 Activity 中的 onConfigurationChanged()。
不要忘记添加清单标签 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。他们为你做。
还有另一种方法可以防止设置字体大小更改导致应用布局问题/字体问题。 你可以试试
// ignore the font scale here
final Configuration newConfiguration = new Configuration(
newBase.getResources().getConfiguration()
);
newConfiguration.fontScale = 1.0f;
applyOverrideConfiguration(newConfiguration);
newBase 来自 attachBaseContext 函数。 您需要在 Activity 中覆盖此回调。
但是,副作用是如果你想使用动画(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);
}
}
您只需要覆盖 attachBaseContext 活动方法并在那里更新配置。
getBaseContext().getResources().updateConfiguration() 已被弃用,尽管此方法有很多示例。如果您在 IDE 警告之外使用此方法,您可能会发现应用的某些部分未缩放。
【讨论】:
在 Android 8.1 (API 27) 上,以前的答案都不适合我。以下是有效的方法:将以下代码添加到您的活动中:
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() 的最后一行
您可以使用基本活动配置强制应用的文本大小,使所有活动成为固有基本活动。 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);
}
【讨论】:
我认为使用dp 是最好的方法,但在某些情况下您可能想要使用字体样式。但是,样式使用sp,您可以通过以下方式将sp转换为dp:
fun TextView.getSizeInSp() = textSize / context.resources.displayMetrics.scaleDensity
fun TextView.convertToDpSize() = setTextSize(TypedValue.COMPLEX_UNIT_DIP, getSizeInSp())
因此,您可以使用样式中的sp 值,无需动态字体大小,也无需对字体大小进行硬编码
【讨论】:
我遇到了同样的问题,并通过将 .XML 文件中的 sp 更改为 dp 来修复它。但是,我还需要修复 WebViews 的文本大小。
通常,要调整WebView 的文本大小,使用setDefaultFontSize() 函数。但是,它的默认值单位是sp。
在我的项目中,我使用setTextZoom(100) 来修复 WebView 的文本和图标大小。*(stackoverflow 中还有其他方法,但几乎所有方法都不推荐使用)*
WebSettings settings = mWebView.getSettings();
settings.setTextZoom(100);
【讨论】: