Что DisplayMetrics.scaledDensity действительно возвращается в Android?

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

sp

Масштабируемые пиксели - это похоже на блок 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?

Обновление

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

enter image description here

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

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, я не получаю правильные значения.

Ответ 1

Я смог понять это сам. После копания в исходном коде 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);

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

enter image description here

Ответ 2

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

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

Я надеюсь, что это поможет!