Что на самом деле возвращает DisplayMetrics.scaledDensity в Android?

Я пытаюсь понять модуль sp в Android, но не могу понять, как он работает. В документации сказано:

зр

Пиксели, не зависящие от масштаба - это похоже на единицу измерения dp, но также масштабируется в соответствии с предпочтениями размера шрифта пользователя. Рекомендуется использовать этот блок при указании размеров шрифта, чтобы они были отрегулированы как по плотности экрана, так и по предпочтениям пользователя.

Используя DisplayMetrics, можно получить scaledDensity, который:

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

Поэтому, учитывая эту информацию, я предполагаю, что scaledDensity будет изменяться при изменении размера системного шрифта и что я могу использовать эту информацию, чтобы узнать соотношение между 1dp и 1sp (scaledDensity / density).

Однако, когда я тестирую это на своем телефоне (Nexus 4), я не получаю ожидаемых результатов. DisplayMetrics.scaledDensity всегда возвращает одно и то же значение (равное DisplayMetrics.density) независимо от того, какой размер шрифта я настроил для своего телефона (через settings -> accessibility -> use large font или же settings -> display -> font size).

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

Код, который я использую для получения scaledDensity:

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
metrics.scaledDensity

Я делаю что-то не так или неправильно понял, что на самом деле делает DisplayMetrics.scaledDensity?

Обновить

Вот три скриншота, которые иллюстрируют то, что я имею в виду:

введите описание изображения здесь

И это код для приложения:

DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);

String result = "\n"
    + "density : " + metrics.density + " (24 dp = " + 24 * metrics.density + " px)\n"
    + "scaledDensity: " + metrics.scaledDensity + " (24 sp = " + 24 * metrics.scaledDensity + " px)\n";

((TextView) this.findViewById(R.id. label)).setText(result);
((TextView) this.findViewById(R.id. sp)).setTextSize(android.util.TypedValue. COMPLEX_UNIT_SP , 24);
((TextView) this.findViewById(R.id.dp )).setTextSize(android.util.TypedValue. COMPLEX_UNIT_DIP, 24);

Я хочу иметь возможность рассчитать количество пикселей, необходимых по оси Y, чтобы нарисовать определенную строку. Если я укажу размер строки с помощью dp, я могу легко рассчитать необходимые пиксели, используя displayMetrics.density, Но когда я использую sp и пытаюсь рассчитать количество пикселей, используя displayMetrics.scaledDensity Я не получаю правильные значения.

2 ответа

Решение

Я смог понять это сам. После поиска исходного кода Android я обнаружил, что scaledDensity свойство рассчитывается как scaledDensity = density * fontScale где fontScale зависит от системных настроек размера шрифта. Так что это было так, как я и думал.

Причина по которой scaledDensity У меня всегда было одно и то же значение, независимо от размера шрифта, потому что я использовал DisplayMetrics для неправильного отображения. Я получил DisplayMetrics от дисплея по умолчанию и в связи с этим документация говорит:

Возвращает отображение, на котором этот экземпляр WindowManager будет создавать новые окна.

Несмотря на название этого метода, возвращаемое отображение не обязательно является основным отображением системы (см. DEFAULT_DISPLAY). Вместо этого возвращаемое отображение может быть вторичным отображением, которым управляет этот экземпляр оконного менеджера. Думайте об этом как о дисплее, который этот экземпляр WindowManager использует по умолчанию.

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

Со следующим обновленным кодом, который использует getResources().getDisplayMetrics() все работает как положено:

DisplayMetrics metrics = getResources().getDisplayMetrics();
String result = "\n"
    + "density : " + metrics.density + " (24 dp = " + 24 * metrics.density + " px)\n"
    + "scaledDensity: " + metrics.scaledDensity + " (24 sp = " + 24 * metrics.scaledDensity + " px)\n" ;

((TextView) this.findViewById(R.id.label)).setText(result);
((TextView) this.findViewById(R.id.sp)).setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
((TextView) this.findViewById(R.id.dp)).setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24);

При таком способе получения метрик отображения это результат, который я получаю (посмотрите, как масштабированная плотность изменяется с размером шрифта):

введите описание изображения здесь

Масштабная плотность - это единица измерения, а не значение размера шрифта. Позвольте мне привести вам пример, не стесняйтесь прокомментировать, если вам нужно больше объяснений.

Скажем, у вас есть размер шрифта 18sp. Это должно быть преобразовано в пиксели для отображения на экране. Следующий расчет сделан: pixels = fontSize * scaledDensity * scale, Теперь, когда вы меняете настройки своего телефона, вы меняете масштаб. Это приводит к тому же самому вычислению, но, скажем, теперь с масштабом 1,5 вместо 1. Единица scaledDensity не изменяется, потому что плотность пикселей вашего экрана не изменилась.

Надеюсь, это поможет!

Другие вопросы по тегам