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