I'm developing an app with React Native for both iOS and Android, and I am trying to prevent device-specific scaling of the display in the app.
For text/font size scaling, putting the following code in the root-level App.js file solves the issue for both iOS and Android:
if (Text.defaultProps == null) {
Text.defaultProps = {};
}
Text.defaultProps.allowFontScaling = false;
However, Android devices have the following Display size setting that is still being applied:
I've tried (unsuccessfully) to piece together a variety of "solutions" to this issue that I've found in answers to the following questions:
Change the system display size programatically Android N
Disabling an app or activity zoom if Setting -> Display -> Display size changed to Large or small
how to prevent system font-size changing effects to android application?
I've often found references to a BaseActivity
class that extends the Activity
class. My understanding is that it is inside of that class where I would be writing a method (let's call it adjustDisplayScale
) to make changes to the Configuration
of the Context
that I get from Resources
, and that then I would be calling adjustDisplayScale
within the onCreate()
method after super.onCreate()
in the MainApplication.java
file.
As of now, in this directory I just have two files - MainApplication.java
and MainActivity.java
.
I've attempted creating a new Module and associated Package file to implement adjustDisplayScale
following these instructions and it did not work:
https://facebook.github.io/react-native/docs/text.html
I've attempted placing implementing the functionality of adjustDisplayScale
within the onCreate()
like this and it did not work:
@Override
public void onCreate() {
super.onCreate();
Context context = getApplicationContext();
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
configuration.fontScale = 1f;
DisplayMetrics metrics = res.getDisplayMetrics();
WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity = 1f;
configuration.densityDpi = (int) res.getDisplayMetrics().xdpi;
context = context.createConfigurationContext(configuration);
SoLoader.init(this, /* native exopackage */ false);
}
A potentially promising answer included the following:
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);
}
But when I tried to utilize this, I got errors about not recognizing the symbol @base.
Some background... I've done 99% of my work on this project in JavaScript / React Native and I have almost no understanding about things like Resources
, Context
, Configuration
, and DisplayMetrics
associated with Android development AND the last time I wrote code in Java was 10 years ago. I've spent a number of agonizing hours trying to figure this out and any help would be greatly appreciated.
ps. I am well-aware that accessibility settings exist for a good reason so please spare me the diatribe I've seen in so many "answers" on why I need to fix my UI to work with accessibility settings rather than disable them.
Simple, use relative layout with the set margin code. Set the margins between each text view, button, etc and it will look the same on every phone. android:layout_marginTop="10dp" // change Top to Bottom, Left or Right for what you need.
Prevent-system-font-size-changing-effects-to-androidForms Android MainActivity. cs, override the Resources and set the configuration to default to restrict the font size effect on application. Resources. UpdateConfiguration() has been deprecated in API 25.
NOTE
I strongly discourage applying such a solution. In a certain way, Screen Zoom just "emulates" different screen sizes and densities in the same device. So, if your app can't handle well a specific screen zoom level, it means that your app may not be displayed correctly on a real screen out there. If your app can't support screen changes, tell the user about it...
There are some docs about screen sizes in the Android Developer and that's how you should handle different screen sizes.
On Android 12, it seems this the context created via context.createConfigurationContext(configuration)
is imuttable. So, you may have problems on Android 12 when rotating the device, for example. context.getResources().getxxxxx()
may return portrait resources (because the context was created in portrait) instead of landscape resources (the new orientation)
Support Different Screen Sizes
supports-screens-element
The answer below is just a "hack" where I tried to circumvent the screen zoom feature. I don't use that on my apps and I strongly recommend dealing with the screen zoom in a more conventional way.
Answer
My first answer does not work if you change the screen resolution. On Samsung devices, you can change the screen zoom but you can also change the screen resolution on some models (Settings->Display->Screen Resolution-> HD, FHD, WQHD etc).
So, I came up with a different code which seems to work with that feature as well. Just, please, note I can't fully test this code since I don't have too many devices to test. On those devices I tested, it seems to work.
One additional note. Ideally, you don't need to use such kind of code to circumvent the screen zoom. In a certain way, the screen zoom is just "simulating" bigger or smaller screens. So, if your app properly supports different screen sizes, you don't need to completely "disable" the screen zoom.
public class BaseActivity extends AppCompatActivity {
@TargetApi(Build.VERSION_CODES.N)
private static final int[] ORDERED_DENSITY_DP_N = {
DisplayMetrics.DENSITY_LOW,
DisplayMetrics.DENSITY_MEDIUM,
DisplayMetrics.DENSITY_TV,
DisplayMetrics.DENSITY_HIGH,
DisplayMetrics.DENSITY_280,
DisplayMetrics.DENSITY_XHIGH,
DisplayMetrics.DENSITY_360,
DisplayMetrics.DENSITY_400,
DisplayMetrics.DENSITY_420,
DisplayMetrics.DENSITY_XXHIGH,
DisplayMetrics.DENSITY_560,
DisplayMetrics.DENSITY_XXXHIGH
};
@TargetApi(Build.VERSION_CODES.N_MR1)
private static final int[] ORDERED_DENSITY_DP_N_MR1 = {
DisplayMetrics.DENSITY_LOW,
DisplayMetrics.DENSITY_MEDIUM,
DisplayMetrics.DENSITY_TV,
DisplayMetrics.DENSITY_HIGH,
DisplayMetrics.DENSITY_260,
DisplayMetrics.DENSITY_280,
DisplayMetrics.DENSITY_XHIGH,
DisplayMetrics.DENSITY_340,
DisplayMetrics.DENSITY_360,
DisplayMetrics.DENSITY_400,
DisplayMetrics.DENSITY_420,
DisplayMetrics.DENSITY_XXHIGH,
DisplayMetrics.DENSITY_560,
DisplayMetrics.DENSITY_XXXHIGH
};
@TargetApi(Build.VERSION_CODES.P)
private static final int[] ORDERED_DENSITY_DP_P = {
DisplayMetrics.DENSITY_LOW,
DisplayMetrics.DENSITY_MEDIUM,
DisplayMetrics.DENSITY_TV,
DisplayMetrics.DENSITY_HIGH,
DisplayMetrics.DENSITY_260,
DisplayMetrics.DENSITY_280,
DisplayMetrics.DENSITY_XHIGH,
DisplayMetrics.DENSITY_340,
DisplayMetrics.DENSITY_360,
DisplayMetrics.DENSITY_400,
DisplayMetrics.DENSITY_420,
DisplayMetrics.DENSITY_440,
DisplayMetrics.DENSITY_XXHIGH,
DisplayMetrics.DENSITY_560,
DisplayMetrics.DENSITY_XXXHIGH
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("TESTS", "Dimension: " + getResources().getDimension(R.dimen.test_dimension));
}
@Override
protected void attachBaseContext(final Context baseContext) {
Context newContext = baseContext;
// Screen zoom is supported from API 24+
if(Build.VERSION.SDK_INT >= VERSION_CODES.N) {
Resources resources = baseContext.getResources();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
Configuration configuration = resources.getConfiguration();
Log.v("TESTS", "attachBaseContext: currentDensityDp: " + configuration.densityDpi
+ " widthPixels: " + displayMetrics.widthPixels + " deviceDefault: " + DisplayMetrics.DENSITY_DEVICE_STABLE);
if (displayMetrics.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE) {
// display_size_forced exists for Samsung Devices that allow user to change screen resolution
// (screen resolution != screen zoom.. HD, FHD, WQDH etc)
// This check can be omitted.. It seems this code works even if the device supports screen zoom only
if(Settings.Global.getString(baseContext.getContentResolver(), "display_size_forced") != null) {
Log.v("TESTS", "attachBaseContext: This device supports screen resolution changes");
// density is densityDp / 160
float defaultDensity = (DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT);
float defaultScreenWidthDp = displayMetrics.widthPixels / defaultDensity;
Log.v("TESTS", "attachBaseContext: defaultDensity: " + defaultDensity + " defaultScreenWidthDp: " + defaultScreenWidthDp);
configuration.densityDpi = findDensityDpCanFitScreen((int) defaultScreenWidthDp);
} else {
// If the device does not allow the user to change the screen resolution, we can
// just set the default density
configuration.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
}
Log.v("TESTS", "attachBaseContext: result: " + configuration.densityDpi);
newContext = baseContext.createConfigurationContext(configuration);
}
}
super.attachBaseContext(newContext);
}
@TargetApi(Build.VERSION_CODES.N)
private static int findDensityDpCanFitScreen(final int densityDp) {
int[] orderedDensityDp;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
orderedDensityDp = ORDERED_DENSITY_DP_P;
} else if(Build.VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
orderedDensityDp = ORDERED_DENSITY_DP_N_MR1;
} else {
orderedDensityDp = ORDERED_DENSITY_DP_N;
}
int index = 0;
while (densityDp >= orderedDensityDp[index]) {
index++;
}
return orderedDensityDp[index];
}
}
ORIGINAL ANSWER
You can try following code (overriding attachBaseContext
). This will "disable" the screen zoom in on your app. This is the way to re-scale the whole screen at once.
@Override
protected void attachBaseContext(final Context baseContext) {
Context newContext;
if(Build.VERSION.SDK_INT >= VERSION_CODES.N) {
DisplayMetrics displayMetrics = baseContext.getResources().getDisplayMetrics();
Configuration configuration = baseContext.getResources().getConfiguration();
if (displayMetrics.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE) {
// Current density is different from Default Density. Override it
configuration.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
newContext = baseContext.createConfigurationContext(configuration);
} else {
// Same density. Just use same context
newContext = baseContext;
}
} else {
// Old API. Screen zoom not supported
newContext = baseContext;
}
super.attachBaseContext(newContext);
}
On that code, I check if the current density is different from the Device's default density. If they are different, I create a new context using default density (and not the current one). Then, I attach this modified context.
You must do that on every Activity
. So, you can create a BaseActivity
and add that code there. Then, you just need to update your activities in order to extend BaseActivity
public class BaseActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(final Context baseContext) {
....
}
}
Then, in your activities:
public class MainActivity extends BaseActivity {
// Since I'm extending BaseActivity, I don't need to add the code
// on attachBaseContext again
// If you don't want to create a base activity, you must copy/paste that
// attachBaseContext code into all activities
}
I tested this code with:
Log.v("Test", "Dimension: " + getResources().getDimension(R.dimen.test_dimension));
Different Screen Zoom (using that code):
2019-06-26 16:38:17.193 16312-16312/com.test.testapplication V/Test: Dimension: 105.0
2019-06-26 16:38:35.545 16312-16312/com.test.testapplication V/Test: Dimension: 105.0
2019-06-26 16:38:43.021 16579-16579/com.test.testapplication V/Test: Dimension: 105.0
Different Screen Zoom (without that code):
2019-06-26 16:42:53.807 17090-17090/com.test.testapplication V/Test: Dimension: 135.0
2019-06-26 16:43:19.381 17090-17090/com.test.testapplication V/Test: Dimension: 120.0
2019-06-26 16:44:00.125 17090-17090/com.test.testapplication V/Test: Dimension: 105.0
So, using that code, I can get the same dimension in pixels regardless of the zoom level.
Edit
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With