Единицы измерения. Чем отличается dp (dip) от px. Screen Density.
Для указания ширины, высоты и отступов View-элементов используются различные единицы измерения (ЕИ):
dp или dip - Density-independent Pixels. Абстрактная ЕИ, позволяющая приложениям выглядеть одинаково на различных экранах и разрешениях.
sp - Scale-independent Pixels. То же, что и dp, только используется для размеров шрифта в View элементах
pt - 1/72 дюйма, определяется по физическому размеру экрана. Эта ЕИ из типографии.
px – пиксел, не рекомендуется использовать т.к. на разных экранах приложение будет выглядеть по-разному.
mm – миллиметр, определяется по физическому размеру экрана
in – дюйм, определяется по физическому размеру экрана
Давайте разбираться, чем они отличаются друг от друга.
in, mm и pt – неизменны относительно друг друга. Всегда 1 in = 25,4 mm и 1 in = 72 pt. Это классические единицы измерения. Т.е. задаете, например, кнопке ширину = 1 in и она должна отобразиться шириной в один дюйм, независимо от разрешения и диагонали экрана.
Что такое px, думаю, тоже понятно. Если у вас есть устройство с экраном шириной 480 px и вы создали кнопку шириной 240 px, то эта кнопка займет в ширину ровно пол-экрана. Но если вы откроете ваше приложение на устройстве с экраном с меньшим разрешением, то соотношение изменится, например:
- если разрешение 320х240, ширина экрана = 240 px. Кнопка займет уже не пол-экрана в ширину, а весь экран
- если же разрешение 1280х800, ширина = 800 px. Кнопка опять же будет занимать в ширину не пол-экрана, а чуть меньше трети
А ведь экран приложения – это обычно не одна кнопка, а набор из многих элементов и все они будут расползаться или сжиматься на разных разрешениях. Поэтому использовать px при разработке НЕ рекомендуется.
Для того, чтобы избежать таких ситуаций на разных разрешениях рекомендуется использовать dp (и sp). Его можно определить, как масштабируемый px. За степень масштабируемости отвечает Screen Density. Это коэффициент, который используется системой для вычисления значения dp. На текущий момент есть 5 значений этого коэффициента:
- low (ldpi) = 0,75
- medium (mdpi) = 1
- tv (tvdpi) = 1,33
- high (hdpi) = 1,5
- extra high (xhdpi) = 2
Т.е. когда для экрана стоит режим mdpi, то 1 dp = 1 px. Т.е. кнопка шириной 100 dp будет выглядеть также как и кнопка шириной 100 px.
Если, например, у нас экран с низким разрешением, то используется режим ldpi. В этом случае 1 dp = 0,75 px. Т.е. кнопка шириной 100 dp будет выглядеть так же как кнопка шириной 75 px.
Если у нас экран с высоким разрешением, то используется режим hdpi или xhdpi. 1 dp = 1, 5 px или 2 px. И кнопка шириной 100 dp будет выглядеть так же как кнопка шириной 150 px или 200 px.
Т.е. при различных разрешениях используются различные Density режимы, которые позволяют приложениям масштабироваться и выглядеть если не одинаково, то, по крайне мере, похоже на всех экранах.
Рассмотрим пример. Предположим у нас есть три устройства (характеристики реальны и взяты из спецификаций):
HTC Wildfire S: 3,2 inch, 480x320 px, 180 dpi
HTC Desire: 3,7 inch, 800x480 px, 252 dpi
Samsung Galaxy Tab: 10 inch, 1280x800 px, 151 dpi
Я создам такой экран:
Это несколько кнопок, в которых ширина и размер шрифта определены с использованием разных единиц измерения. На каждой кнопке для наглядности я написал ее ширину (layout_width) и размер шрифта (textSize) через запятую. Обратите внимание, что ширина всех кнопок кроме последней одинакова. Так происходит потому, что 1 in = 72 pt = 25,4 mm в любом случае, а для данного экрана также 1 in = 252 px = 252 dp. Шрифты также везде одинаковы, т.к. размер шрифта по умолчанию равен 14 sp и в данном случае равен 14 px.
xml-код:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/button1"
android:layout_height="wrap_content"
android:layout_width="1in"
android:text="1 inch, default">
</Button>
<Button
android:id="@+id/button2"
android:layout_height="wrap_content"
android:layout_width="72pt"
android:text="72 pt, default">
</Button>
<Button
android:id="@+id/button3"
android:layout_height="wrap_content"
android:layout_width="25.4mm"
android:text="25.4 mm, default">
</Button>
<Button
android:id="@+id/button4"
android:layout_height="wrap_content"
android:layout_width="252px"
android:text="252 px, 14 px"
android:textSize="14px">
</Button>
<Button
android:id="@+id/button5"
android:layout_height="wrap_content"
android:layout_width="252dp"
android:textSize="14sp"
android:text="252 dp, 14 sp">
</Button>
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="wrap_content, default">
</Button>
</LinearLayout>
Посмотрим, как это будет выглядеть на экранах других устройств:
Для всех экранов я поставил режим mdpi. Скриншоты экранов смасштабированы к одному размеру для наглядности. Надо понимать, что на самом деле диагонали экранов существенно отличаются, а наша цель – добиться, чтобы приложение выглядело одинаково на различных устройствах.
Мы видим, что на всех экранах 1 px остался равен 1 dp (т.к. режим mdpi). И in, pt и mm сохранили свои пропорции (1; 72; 25,4) относительно друг друга. Но обратите внимание, что 1 in уже не равен 252 px на экранах Wildfire и Galaxy. Это потому, что у этих устройств другое соотношение диагонали экрана и разрешения:
- у Wildfire экран с dpi = 180, т.е. 1 in = 180 px, поэтому первая кнопка (с шириной = 1 inch) теперь короче, чем кнопка с шириной 252 px.
- для Galaxy, соответственно, dpi = 151.
Видно, что приложение выглядит достаточно по разному на трех экранах. Так было бы, если бы не существовало коэфициента Screen Density. Но он есть и давайте смотреть, чем он полезен. Я включу режим ldpi для Wildfire и xhdpi для Galaxy. Desire оставляю в mdpi.
Ширина кнопок указанная в in, mm, pt неизменна, на эти единицы измерения режимы Density влияния не имеют. Нам интересны четвертая и пятая кнопки. Видим, что на экранах Wildfire и Galaxy отличаются размеры px и dp, т.к. мы сменили mdpi на ldpi и xhdpi. Для Wildfire 1 dp стал равен 0,75 px, а для Galaxy 1 dp = 2 px. Также видим, что изменился шрифт на кнопках, где размер шрифта был указан по умолчанию или в sp-единицах. Он так же, как и dp смасштабировался благодаря Density режимам. А шрифт, размер которого был указан в px (четвертая кнопка) оставался неизменным и на Wildfire выглядит крупным, а на Galaxy – мелким.
Отлично видно, что адекватнее всего перенос на другие экраны перенесли пятая и шестая кнопки. Для пятой кнопки используются dp и sp. Для шестой кнопки – ширина = wrap_content и размер шрифта по умолчанию. А кнопки с in, mm, pt и px статичны и на разных экранах выглядят по-разному. Наверняка, есть случаи, когда необходимо использовать именно эти единицы измерения. Но в основном старайтесь использовать dp (для ширины, высоты и т.д.) и sp (для размера шрифта).
Конечно Density не дает масштабирования абсолютно пропорциального разнице в разрешениях экрана. Погрешность есть, но она невелика и является вполне приемлемой платой за способность приложения «сохранять форму» на разных устройствах.
Кем именно устанавливаются Density режимы для различных экранов – я не знаю. Но подозреваю, что производителями устройств/экранов. Еще мне интересно, можно ли эти режимы переключать при работе устройства. Думаю, чуть позже я найду ответы на эти вопросы.
Для создания этого материала я использовал различные конфигурации экранов, которые можно создавать самому:
Вы наверно обратили внимание, что в папке Андроид-проекта в Eclipse есть папки drawable c суффиксами hdpi, ldpi, mdpi:
Папка drawable используется для хранения изображений, а суффиксы дают понять системе из какой именно drawable использовать картинки при текущем Density режиме. Подробнее об этом можно почитать в хелпе. Кстати, там же вы найдете уже изученный нами –land, который связан с горизонтальной ориентацией экрана. Будем по мере изучения Андроид знакомиться с остальными.