Что на самом деле возвращает 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 не изменяется, потому что плотность пикселей вашего экрана не изменилась.
Надеюсь, это поможет!