Как предотвратить изменение размера дисплея устройства Android в моем приложении?

Я разрабатываю приложение с React Native как для iOS, так и для Android, и пытаюсь предотвратить масштабирование дисплея в приложении для конкретного устройства.

Для масштабирования размера текста / шрифта размещение следующего кода в файле App.js корневого уровня решает проблему как для iOS, так и для Android:

if (Text.defaultProps == null) {
    Text.defaultProps = {};
}
Text.defaultProps.allowFontScaling = false;

Тем не менее, устройства Android имеют следующую настройку размера экрана, которая все еще применяется:

Я пытался (безуспешно) собрать воедино различные "решения" этой проблемы, которые я нашел в ответах на следующие вопросы:

Изменение размера дисплея системы программно Android N

Отключение приложения или масштабирования активности, если Настройки -> Дисплей -> Размер дисплея изменен на Большой или маленький

Как предотвратить эффекты изменения размера шрифта системы для приложений Android?

Я часто встречал ссылки на BaseActivity класс, который расширяет Activity класс. Насколько я понимаю, это внутри того класса, где я буду писать метод (давайте назовем его adjustDisplayScale) внести изменения в Configuration из Context что я получаю от Resources и что тогда я буду звонить adjustDisplayScale в пределах onCreate() метод после super.onCreate() в MainApplication.java файл.

На данный момент в этом каталоге у меня есть только два файла - MainApplication.java а также MainActivity.java,

Я попытался создать новый модуль и связанный файл пакета для реализации adjustDisplayScale следуя этим инструкциям, и это не сработало: https://facebook.github.io/react-native/docs/text.html

Я попытался разместить реализацию функциональности adjustDisplayScale в пределах onCreate() вот так и не получилось

@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);
}

Потенциально многообещающий ответ включал следующее:

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);
}

Но когда я попытался использовать это, я получил ошибки из-за того, что не распознал символ @base.

Немного предыстории... Я выполнил 99% своей работы над этим проектом в JavaScript / React Native, и у меня почти нет понимания таких вещей, как Resources, Context, Configuration, а также DisplayMetrics связан с разработкой Android И последний раз, когда я писал код на Java, было 10 лет назад. Я провел несколько мучительных часов, пытаясь понять это, и любая помощь будет принята с благодарностью.

пс. Я хорошо знаю, что настройки доступности существуют по уважительной причине, поэтому, пожалуйста, избавьте меня от диатрибы, которую я видел во многих "ответах" о том, почему мне нужно исправить свой пользовательский интерфейс, чтобы работать с настройками доступности, а не отключать их.

3 ответа

Вы можете попробовать следующий код (переопределение attachBaseContext). Это "отключит" масштабирование экрана в вашем приложении. Это способ масштабировать весь экран сразу.

@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
            displayMetrics.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
            configuration.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
            newContext = baseContext;//baseContext.createConfigurationContext(configuration);
        } else {
            // Same density. Just use same context
            newContext = baseContext;
        }
    } else {
        // Old API. Screen zoom not supported
        newContext = baseContext;
    }
    super.attachBaseContext(newContext);
}

В этом коде я проверяю, отличается ли плотность тока от плотности устройства по умолчанию. Если они отличаются, я создаю новый контекст, используя плотность по умолчанию (а не текущий). Затем я присоединяю этот измененный контекст.

Вы должны сделать это на каждом Activity, Итак, вы можете создать BaseActivity и добавьте этот код там. Затем вам просто нужно обновить свою деятельность, чтобы расширить BaseActivity

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void attachBaseContext(final Context baseContext) {
        ....
    }
}

Тогда в вашей деятельности:

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
}

Я проверил этот код с:

Log.v("Test", "Dimension: " + getResources().getDimension(R.dimen.test_dimension));

Другой масштаб экрана (с использованием этого кода):

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

Другой масштаб экрана (без этого кода):

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

Таким образом, используя этот код, я могу получить одно и то же измерение в пикселях независимо от уровня масштабирования.

редактировать

Упрощенная версия метода @guipivoto в сочетании с масштабированием шрифта.

      @Override
protected void attachBaseContext(Context newBase) {
    final Configuration configuration = newBase.getResources().getConfiguration();

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
    {
        final DisplayMetrics displayMetrics = newBase.getResources().getDisplayMetrics();
        if (displayMetrics.densityDpi != DisplayMetrics.DENSITY_DEVICE_STABLE)
        {
            // Current density is different from Default Density. Override it
            configuration.densityDpi = DisplayMetrics.DENSITY_DEVICE_STABLE;
        }
    }
    configuration.fontScale = 1.0f;
    Context newContext = newBase.createConfigurationContext(configuration);
    super.attachBaseContext(newContext);
}

мы можем использовать код раздела

      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);

если вы нашли это полезным, вы можете проголосовать за THIS_ANSWER.