简体   繁体   English

如何防止 Android 设备在我的应用程序中显示尺寸缩放?

[英]How do I prevent Android device Display size scaling in my app?

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.我正在为 iOS 和 Android 开发一个带有 React Native 的应用程序,并且我试图阻止应用程序中特定于设备的显示缩放。

For text/font size scaling, putting the following code in the root-level App.js file solves the issue for both iOS and Android:对于文本/字体大小缩放,将以下代码放在根级 App.js 文件中可以解决 iOS 和 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:但是,Android 器件具有以下仍在应用的显示尺寸设置:

Android 设备显示大小设置

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 以编程方式更改系统显示大小 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? 如何防止系统字体大小变化对 android 应用程序的影响?

I've often found references to a BaseActivity class that extends the Activity class.我经常发现对扩展Activity class 的BaseActivity 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.我的理解是它在adjustDisplayScale内部,我将在其中编写一个方法(我们称之为adjustDisplayScale )来更改我从Resources获得的ContextConfiguration ,然后我将在onCreate()MainApplication.java文件中的super.onCreate()之后的方法。

As of now, in this directory I just have two files - MainApplication.java and MainActivity.java .到目前为止,在这个目录中我只有两个文件 - MainApplication.javaMainActivity.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 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:我已经尝试在onCreate()中像这样实现adjustDisplayScale的功能,但它不起作用:

@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.但是当我尝试使用它时,我收到了关于无法识别符号 @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.一些背景知识...我在 JavaScript / React Native 中完成了该项目 99% 的工作,我几乎不了解与 Android 开发和上次编写代码相关的ResourcesContextConfigurationDisplayMetrics等内容在 Java 是 10 年前。 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.我很清楚可访问性设置的存在是有充分理由的,所以请不要对我在这么多“答案”中看到的关于为什么我需要修复我的 UI 以使用可访问性设置而不是禁用它们的谩骂。

UPDATE更新

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).在三星设备上,您可以更改屏幕缩放,但您也可以更改某些型号的屏幕分辨率(设置->显示->屏幕分辨率-> HD、FHD、WQHD 等)。

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 ).您可以尝试以下代码(覆盖attachBaseContext )。 This will "disable" the screen zoom in your app.这将“禁用”应用程序中的屏幕缩放。 This is the way to re-scale 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 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 .您必须在每个Activity上都这样做。 So, you can create a BaseActivity and add that code there.因此,您可以创建一个BaseActivity并在那里添加该代码。 Then, you just need to update your activities in order to extend BaseActivity然后,您只需要更新您的活动以扩展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 the zoom level.因此,使用该代码,无论缩放级别如何,我都可以获得相同的像素尺寸。

Edit编辑

we can use code of section我们可以使用部分代码

Configuration configuration = getResources().getConfiguration();
configuration.fontScale = (float) 1; //0.85 small size, 1 normal size, 1,15 big etc
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
configuration.densityDpi = (int) getResources().getDisplayMetrics().xdpi;
getBaseContext().getResources().updateConfiguration(configuration, metrics);

if you found this helpful you can upvote THIS_ANSWER .如果你觉得这有帮助,你可以投票支持 THIS_ANSWER

How to prevent the particular view enlarging with display size in jetpack compose.如何防止特定视图在 jetpack compose 中随显示尺寸放大。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM