Background: Android N comes with a feature to change system Display Size from settings, in addition to the previously present feature of changing Font Size.
Change Display Size:
Image Source: pcmag.com
Question:
If an app has android.permission.WRITE_SETTINGS permission to change the settings, there are ways to change the system font size programatically as mentioned in How to programmatically change font settings of Device: font style and font size?. However I couldn't find a way to change the display size programmatically. Is it possible?
What I've tried?
I've checked the possible options in the list of Settings.System convenience functions provided for changing settings programmatically.
Update:
I've opened a feature request for the same here: https://code.google.com/p/android/issues/detail?id=214124 . If you feel it would be useful please star it.
Just share my approach to tackle this requirement. I achieve this function by using the dirty Java reflection method - although it's not that elegant.
The main reference source code files are:
ScreenZoomSettings.java (https://github.com/aosp-mirror/platform_packages_apps_settings/blob/master/src/com/android/settings/display/ScreenZoomSettings.java)
DisplayDensityUtils.java (http://androidxref.com/9.0.0_r3/xref/frameworks/base/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java)
WindowManagerGlobal.java (http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/view/WindowManagerGlobal.java)
And, follow the below steps, then you can get the required control:
Read ZoomScreenSettings.java's onCreate() and commit(). They demonstrate how to correctly get and set the density value into the framework.
Read DisplayDensityUtils.java. It shows how to use WindowManagerService to control the system density. Because we can't get the instance of DisplayDensityUtils via reflection, we need to understand which WindowManagerService methods are utilized.
Use reflection to get WindowManagerService's instance, and write a DisplayDensityUtils-like class into your project.
// Where wm is short for window manager
val wmGlobalClz = Class.forName("android.view.WindowManagerGlobal")
val getWmServiceMethod = wmGlobalClz.getDeclaredMethod("getWindowManagerService")
val wmService = getWmServiceMethod.invoke(wmGlobalClz)
val wmInterfaceClz = Class.forName("android.view.IWindowManager")
// Now, we already have the ability to do many things we want.
// For instance, to get the default density value.
val getInitialDisplayDensityMethod = wmInterfaceClz.getDeclaredMethod(
"getInitialDisplayDensity",
Integer.TYPE
)
val defaultDensity = getInitialDisplayDensityMethod.invoke(
wmService,
Display.DEFAULT_DISPLAY
) as Int
Set or get the density value with your DisplayDensityUtils-like class. One thing just to mention is that, if you want to pass an index value (e.g., 2 for large display size), please feed it to your DisplayDensityUtils-like class's mValues array to get the actual density value which is the right one to pass to the framework. Getting the current density index also applies the same concept.
In manifest, under application: android:configChanges="density"
In an activity/application:
public void adjustDisplayScale(Configuration configuration) {
if (configuration != null) {
Log.d("TAG", "adjustDisplayScale: " + configuration.densityDpi);
if(configuration.densityDpi >= 485) //for 6 inch device OR for 538 ppi
configuration.densityDpi = 500; //decrease "display size" by ~30
else if(configuration.densityDpi >= 300) //for 5.5 inch device OR for 432 ppi
configuration.densityDpi = 400; //decrease "display size" by ~30
else if(configuration.densityDpi >= 100) //for 4 inch device OR for 233 ppi
configuration.densityDpi = 200; //decrease "display size" by ~30
DisplayMetrics metrics = getResources().getDisplayMetrics();
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = configuration.densityDpi * metrics.density;
this.getResources().updateConfiguration(configuration, metrics);
}
}
Call it just after super.onCreate(..):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
adjustDisplayScale( getResources().getConfiguration());
This will set the Display Size in-between the "Smaller" and "Small" settings of "Display Size" overriding any Display Size settings set by user.
It self-adjusts correctly for 4in, 5.5in, 6in etc devices.. but I'm sure there would a better way than using those if statements.
While referring Settings.System , there is a [putConfiguration(ContentResolver cr, Configuration config)](https://developer.android.com/reference/android/provider/Settings.System.html#putConfiguration(android.content.ContentResolver, android.content.res.Configuration)) method.
Use of this method is:
Convenience function to write a batch of configuration-related settings from a Configuration object.
In Configuration
This includes both user-specified configuration options (locale list and scaling) as well as device configurations (such as input modes, screen size and screen orientation).
Set configuration with SCREENLAYOUT_SIZE_MASK values for screen size.
It is:
The SCREENLAYOUT_SIZE_MASK bits define the overall size of the screen. They may be one of SCREENLAYOUT_SIZE_SMALL, SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_LARGE, or SCREENLAYOUT_SIZE_XLARGE.
I hope its helps you.
Related
I'm trying to change the display resolution of my primary display on Android 8.1. I get an event from the kernel (based on EDID) that tells me I need to change mode.
I then basically do:
sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(ISurfaceComposer::eDisplayIdMain));
status_t res = SurfaceComposerClient::setActiveConfig(display, new_mode);
This updates surfaceflinger, the display hal and the kernel correctly, but windowmanager/display does not change resolution until I run:
wm size 1920x1080
wm density 240
But wm calls these function, which seem a bit too harsh, and also store the size/density in persistent setting:
public void setForcedDisplaySize(int displayId, int width, int height);
public void setForcedDisplayDensityForUser(int displayId, int density, int userId);
I've been looking at code in windowmanager/display without finding an API that I should use for this.
Is there a way to notify windowmanager/display about the change in surfaceflinger, or an API I've missed that I can use?
Am I going about this the wrong way? Is there a proper way you're meant to change resolution/mode of the primary display, that I haven't found?
I recently finished developing my android application. I used sp (Scaled pixels) for all textSize. The problem is when i adjusted the system font-size, my application's font-sizes are changing. I can use dp (Device independent pixels) but it will take too long to maintain my application.
I referenced text size from this.
Is there a way to prevent system font-size changing effects to my application ?
If you require your text to remain the same size, you'll have to use dp.
To quote the documentation:
An sp is the same base unit, but is scaled by the user's preferred text size (it’s a scale-independent pixel), so you should use this measurement unit when defining text size (but never for layout sizes).
Emphasis mine.
So you're seeing the expected behaviour for using sp as your units for text size.
I don't understand what you mean about using dp taking too long to maintain your app - as far as I can tell, it'll exactly be the same amount of effort? (perhaps less, though it'll likely make it less usable for users with poor eyesight)
I recently ran into this problem as well. Our UI didn't scale well on phones with limited screen dimensions and changing the entire UI on the off chance a user set's their Accessibility Options to "Huge" seemed silly.
I found this question on StackOverflow to be most helpful.
What I did was put the following code below in my BaseActivity (an Activity class that all my activities extend from)
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);
}
}
And called it right after my super.onCreate() like so
adjustFontScale(getResources().getConfiguration());
What this code does is identify if the user set their font scale in Accessibility Settings to something greater than 1.30f (1.30f is "Large" on The Note 5, but probably varies a bit from device-to-device). If the user set their font too large ("Extra Large", "Huge"...), we scale the application only to "Large".
This allows your app to scale to a user's preferences (to a degree) without distorting your UI. Hopefully this will help others. Good luck scaling!
Other Tips
If you want certain layouts to scale with your fonts (say...a RelativeLayout that you use as a backdrop against your fonts), you can set their width/height with sp instead of the classic dp. When a user changes their font size, the layout will change accordingly with the fonts in your application. Nice little trick.
None of the previous answers worked for me, on Android 8.1 (API 27). Here's what worked: Add the following code to your activity:
Kotlin Code:
override fun attachBaseContext(newBase: Context?) {
val newOverride = Configuration(newBase?.resources?.configuration)
newOverride.fontScale = 1.0f
applyOverrideConfiguration(newOverride)
super.attachBaseContext(newBase)
}
Java Code:
#Override
protected void attachBaseContext(Context newBase) {
final Configuration override = new Configuration(newBase.getResources().getConfiguration());
override.fontScale = 1.0f;
applyOverrideConfiguration(override);
super.attachBaseContext(newBase);
}
You don't need to change your AndroidManifest.xml.
That's how we do it.
In Application class override onConfigurationChanged() like this.
If you want different behavior for different activities - override onConfigurationChanged() in Activity.
Don't forget to add manifest tag android:configChanges="fontScale" since you are hadnling this configuration change yourself.
#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);
}
In some helper class we have adjustFontScale() method.
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);
}
}
WARNING! That will totally ignore Accessibility Font Scale user settings and will prevent your App fonts scaling!
That's how you do it in 2018 (Xamarin.Android/C# - same approach in other languages):
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);
}
}
All you need is override attachBaseContext method of activity and update config there.
getBaseContext().getResources().updateConfiguration() is deprecated though there're numerous examples with this method. If you use this approach besides the IDE warning you might find some parts of your app not scaled.
There's another way to prevent app layout issue / font issue from the setting font size change.
You can try
// ignore the font scale here
final Configuration newConfiguration = new Configuration(
newBase.getResources().getConfiguration()
);
newConfiguration.fontScale = 1.0f;
applyOverrideConfiguration(newConfiguration);
where newBase is from attachBaseContext function.
You need to override this callback in your Activity.
But, the side effect is that if you wanna use animation (objectanimator/valueanimator), then it will cause the weird behavior.
I encountered the same problem and fixed it by changing sp to dp in .XML file. However, I also need to fix the text size of the WebViews.
Normally, to adjust the text size of the WebView the setDefaultFontSize() function is used. However, its default value unit is sp.
In my project, I used setTextZoom(100) to fix the text and icon size of the WebView.* (There are other methods in stackoverflow but almost all of them are deprecated)*
WebSettings settings = mWebView.getSettings();
settings.setTextZoom(100);
For further details about setTextZoom()
you can force to text size of your app using base activity Configuration, make all activities inherent base activity. 1.0f will force the app font size to normal ignoring system settings.
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);
}
Tried answers about disabling fontScale in whole application. It's working, but I come to answer it's a terrible idea for only one reason:
You don't make your app better for visually impaired people.
Better way (I think) it's allow font scale but with restrictions only in some places, where you can't scale your text for it looks readable.
Realization (10.02.22)
After a day of thinking, I created Kotlin extension for TextView (also may use for EditText because it's a child):
fun TextView.removeFontScale() {
val fontScale = resources.configuration.fontScale
if (fontScale != 1f) {
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale / fontScale
textSize = newTextSize
}
}
Need divide scaledTextSize twice because after setting a new textSize for TextView font scale will happen.
UPDATE / FIX (14.02.22)
Previous solution doesn't work on real devices, only emulator (vice versa it overwise increasing text size). So, I found another way:
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale
setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSize)
Here new textSize for TextView after setting will not be scaled on fontScale value.
Use example:
titleText.removeFontScale()
subtitleText.removeFontScale()
Logs of working, size in 1.3/1.5 scale become like in 1.0 scale:
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
P.S. I notice that standard Toolbar didn't scale fonts and after that I understand that this practice is OK (disable scaling where it's really need).
I think usage of dp is the best way, but in some case you may want to use a font style. However, the style is using sp, you can convert sp to dp by:
fun TextView.getSizeInSp() = textSize / context.resources.displayMetrics.scaleDensity
fun TextView.convertToDpSize() = setTextSize(TypedValue.COMPLEX_UNIT_DIP, getSizeInSp())
So, you can use the sp value from style without dynamic font size, and no need to hardcode the font size
i looked at these 2 function in the documentation here
i want to get the desired wallaper dimensions,
running those functions on an SGS3 (1280x720) with stock launcher,
i got both minDesiredWidth + minDesiredHight: 1280x1280
same thing with a Note 3 (1920x1080) i got 1920x1920
i want to know the desired ratio of wallpaper the device wants, and i thought i would get it from those 2 functions.
both those devices stock launchers have a static background image of their respective screen resolutions, so why does getDesiredMinimumWidth doesn't give me 1280/1080 for each device respectively?
how do i know the proper ratio for the device?
This is the intended result of the methods, the code used in the WallpaperManager class is:
return sGlobals.mService.getHeightHint();
and
return sGlobals.mService.getWidthHint();
It isn't mentioned anywhere why they return the same value, but to get the true values of WxH, you should use:
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getRealSize(displaySize);
and refer to the values with int width = displaySize.x and int height = displaySize.y
I'm developing in qt 5.3 on android device. I can't get the screen resolution.
With the old qt 5 version this code worked:
QScreen *screen = QApplication::screens().at(0);
largh=screen->availableGeometry().width();
alt =screen->availableGeometry().height();
However now it doesn't work (returns a screen size 00x00). Is there another way to do it? thanks
Size holds the pixel resolution
screen->size().width()
screen->size().height();
Whereas, availableSize holds the size excluding window manager reserved areas...
screen->availableSize().width()
screen->availableSize().height();
More info on the QScreen class.
for more information, screen availableSize is not ready at the very beginning, so you have to wait for it, here is the code:
Widget::Widget(QWidget *parent){
...
QScreen *screen = QApplication::screens().at(0);
connect(screen, SIGNAL(virtualGeometryChanged(QRect)), this,SLOT(getScreen(QRect)));
}
void Widget::getScreen(QRect rect)
{
int screenY = screen->availableSize().height();
int screenX = screen->availableSize().width();
this->setGeometry(0,0,screenX,screenY);
}
I found that there are several ways to obtain the device resolution, each outputs the same results and thankfully works across all Os-es supported by Qt...
1) My favorite is to write a static function using QDesktopWidget in a reference class and use it all across the code:
QRect const CGenericWidget::getScreenSize()
{
//Note: one might implement caching of the value to optimize processing speed. This however will result in erros if screen resolution is resized during execution
QDesktopWidget scr;
return scr.availableGeometry(scr.primaryScreen());
}
Then you can just call across your code the function like this:
qDebug() << CGenericWidget::getScreenSize();
It will return you a QRect const object that you can use to obtain the screen size without the top and bottom bars.
2) Another way to obtain the screen resolution that works just fine if your app is full screen is:
QWidget *activeWindow = QApplication::activeWindow();
m_sw = activeWindow->width();
m_sh = activeWindow->height();
3) And of course you have the option that Zeus recommended:
QScreen *screen = QApplication::screens().at(0);
largh=screen->availableSize().width();
alt =screen->availableSize().height();
I recently finished developing my android application. I used sp (Scaled pixels) for all textSize. The problem is when i adjusted the system font-size, my application's font-sizes are changing. I can use dp (Device independent pixels) but it will take too long to maintain my application.
I referenced text size from this.
Is there a way to prevent system font-size changing effects to my application ?
If you require your text to remain the same size, you'll have to use dp.
To quote the documentation:
An sp is the same base unit, but is scaled by the user's preferred text size (it’s a scale-independent pixel), so you should use this measurement unit when defining text size (but never for layout sizes).
Emphasis mine.
So you're seeing the expected behaviour for using sp as your units for text size.
I don't understand what you mean about using dp taking too long to maintain your app - as far as I can tell, it'll exactly be the same amount of effort? (perhaps less, though it'll likely make it less usable for users with poor eyesight)
I recently ran into this problem as well. Our UI didn't scale well on phones with limited screen dimensions and changing the entire UI on the off chance a user set's their Accessibility Options to "Huge" seemed silly.
I found this question on StackOverflow to be most helpful.
What I did was put the following code below in my BaseActivity (an Activity class that all my activities extend from)
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);
}
}
And called it right after my super.onCreate() like so
adjustFontScale(getResources().getConfiguration());
What this code does is identify if the user set their font scale in Accessibility Settings to something greater than 1.30f (1.30f is "Large" on The Note 5, but probably varies a bit from device-to-device). If the user set their font too large ("Extra Large", "Huge"...), we scale the application only to "Large".
This allows your app to scale to a user's preferences (to a degree) without distorting your UI. Hopefully this will help others. Good luck scaling!
Other Tips
If you want certain layouts to scale with your fonts (say...a RelativeLayout that you use as a backdrop against your fonts), you can set their width/height with sp instead of the classic dp. When a user changes their font size, the layout will change accordingly with the fonts in your application. Nice little trick.
None of the previous answers worked for me, on Android 8.1 (API 27). Here's what worked: Add the following code to your activity:
Kotlin Code:
override fun attachBaseContext(newBase: Context?) {
val newOverride = Configuration(newBase?.resources?.configuration)
newOverride.fontScale = 1.0f
applyOverrideConfiguration(newOverride)
super.attachBaseContext(newBase)
}
Java Code:
#Override
protected void attachBaseContext(Context newBase) {
final Configuration override = new Configuration(newBase.getResources().getConfiguration());
override.fontScale = 1.0f;
applyOverrideConfiguration(override);
super.attachBaseContext(newBase);
}
You don't need to change your AndroidManifest.xml.
That's how we do it.
In Application class override onConfigurationChanged() like this.
If you want different behavior for different activities - override onConfigurationChanged() in Activity.
Don't forget to add manifest tag android:configChanges="fontScale" since you are hadnling this configuration change yourself.
#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);
}
In some helper class we have adjustFontScale() method.
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);
}
}
WARNING! That will totally ignore Accessibility Font Scale user settings and will prevent your App fonts scaling!
That's how you do it in 2018 (Xamarin.Android/C# - same approach in other languages):
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);
}
}
All you need is override attachBaseContext method of activity and update config there.
getBaseContext().getResources().updateConfiguration() is deprecated though there're numerous examples with this method. If you use this approach besides the IDE warning you might find some parts of your app not scaled.
There's another way to prevent app layout issue / font issue from the setting font size change.
You can try
// ignore the font scale here
final Configuration newConfiguration = new Configuration(
newBase.getResources().getConfiguration()
);
newConfiguration.fontScale = 1.0f;
applyOverrideConfiguration(newConfiguration);
where newBase is from attachBaseContext function.
You need to override this callback in your Activity.
But, the side effect is that if you wanna use animation (objectanimator/valueanimator), then it will cause the weird behavior.
I encountered the same problem and fixed it by changing sp to dp in .XML file. However, I also need to fix the text size of the WebViews.
Normally, to adjust the text size of the WebView the setDefaultFontSize() function is used. However, its default value unit is sp.
In my project, I used setTextZoom(100) to fix the text and icon size of the WebView.* (There are other methods in stackoverflow but almost all of them are deprecated)*
WebSettings settings = mWebView.getSettings();
settings.setTextZoom(100);
For further details about setTextZoom()
you can force to text size of your app using base activity Configuration, make all activities inherent base activity. 1.0f will force the app font size to normal ignoring system settings.
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);
}
Tried answers about disabling fontScale in whole application. It's working, but I come to answer it's a terrible idea for only one reason:
You don't make your app better for visually impaired people.
Better way (I think) it's allow font scale but with restrictions only in some places, where you can't scale your text for it looks readable.
Realization (10.02.22)
After a day of thinking, I created Kotlin extension for TextView (also may use for EditText because it's a child):
fun TextView.removeFontScale() {
val fontScale = resources.configuration.fontScale
if (fontScale != 1f) {
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale / fontScale
textSize = newTextSize
}
}
Need divide scaledTextSize twice because after setting a new textSize for TextView font scale will happen.
UPDATE / FIX (14.02.22)
Previous solution doesn't work on real devices, only emulator (vice versa it overwise increasing text size). So, I found another way:
val scaledTextSize = textSize
val newTextSize = scaledTextSize / fontScale
setTextSize(TypedValue.COMPLEX_UNIT_PX, newTextSize)
Here new textSize for TextView after setting will not be scaled on fontScale value.
Use example:
titleText.removeFontScale()
subtitleText.removeFontScale()
Logs of working, size in 1.3/1.5 scale become like in 1.0 scale:
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
P.S. I notice that standard Toolbar didn't scale fonts and after that I understand that this practice is OK (disable scaling where it's really need).
I think usage of dp is the best way, but in some case you may want to use a font style. However, the style is using sp, you can convert sp to dp by:
fun TextView.getSizeInSp() = textSize / context.resources.displayMetrics.scaleDensity
fun TextView.convertToDpSize() = setTextSize(TypedValue.COMPLEX_UNIT_DIP, getSizeInSp())
So, you can use the sp value from style without dynamic font size, and no need to hardcode the font size