суббота, 20 августа 2011 г.

Спецификаторы ресурсов

Одной из особенностей разработки приложений под android, является, то что программист не знает на каком устройстве будет запускаться его программа. Устройства android порой различные технические характеристики: разное разрешение экрана, разный язык, наличие или отсутствие тачскрина. Чтобы решить эту проблему система управления ресурсами Android предоставляет ряд спецификаторов(qualifier), позволяющий выбирать тот или иной ресурс в зависимости от характеристик или текущего состояния устройства. Спецификатор это короткая строка в название директории ресурсов для определения когда эти ресурсы должны быть использованы. Часто создаются различные схемы интерфейса для разных размеров и ориентаций экранов устройства. Например, при портретной ориентации кнопки удобнее разместить по вертикали, а для альбомной ориентации - по горизонтали. Чтобы изменить расположение в зависимости от ориентации, можно определить две различные схемы и добавить соответствующие спецификаторы к имени каталога каждого макета. Тогда, система автоматически применяет соответствующий макет в зависимости от текущей ориентации устройства.

Все ресурсы приложения для Android хранятся в директории res/<Тип_ресурса>, например res/drawable (для изображений), res/values (для строк), res/layout (для разметки). Эти каталоги соответствуют ресурсам по умолчанию, если же мы хотим задать отдельные ресурсы, для например больших экранов, то необходимо создать еще одну директорию, добавив к ее названию один или несколько спецификаторов.
Например у нас есть директория с двумя файлами
res/drawable/image1.png
res/drawable/image2.png

для больших экранов необходимо создать еще одну директорию
res/drawable-large/image1.png
res/drawable-large/image2.png

Важно, чтобы имена файлов во всех папках с любыми спецификаторами были одинаковыми. Таким образом идентификатор ресурса который мы будем использовать в программном коде не меняется R.drawable.image1 и R.drawable.image2 соответственно. Собственно для программиста нет никакой разницы на каком устройстве будет запускаться программа, система Android при выполнении программы сама определит нужный ресурс и будет использовать именно его.

Возможные спецификаторы
  1. Язык и регион. Например 'en', 'ru', 'fr', 'en-rUS' и т. д. В последнем случае 'en'- язык, а 'rUS' - регион. Задать отдельно регион без языка нельзя. Язык и регион можно изменить в настройках устройства.
  2. Минимальная ширина. sw<n>dp, (например 'sw320dp', 'sw720dp'. Минимальная ширина не зависит от ориентации экрана, это по сути минимально возможное значение для меньшей стороны экрана. Так файлы из директории res/layout-sw600dp/ будет использоваться, когда и длина и ширина экрана больше 600dp. Это свойство является характеристикой экрана и не может изменяться в настройках устройства. Если для приложения определено несколько каталогов с этим спецификатором, то система выбирает наиболее близкий (без превышения).
  3. Доступная ширина. w<n>dp (например 'w720dp', 'w1024dp'). Определяет минимальную доступную ширину экрана. От предыдущего спецификатора этот отличается тем, что зависит от ориентации экрана. Причем учитывается только доступная ширина, поэтому если часть экрана занята какими-то статическими элементами (например меню телефона), то используется значение меньше реальной ширины на ширину этих элементов. Также как у предыдущего если определено несколько каталогов с этим спецификатором, то система выбирает наиболее близкий (опять же без превышения).
  4. Доступная высота. h<n>dp. Определяет минимальную доступную высоту экрана. Свойства спецификатора аналогичны предыдущему.
  5. Размер экрана. Например 'small' (размер экрана около 320х426dp, это QVGA низкой плотности и VGA высокой плотности); 'normal' (320x470dp, WQVGA низкой плотности, HVGA средней плотности и WVGA высокой плотности); 'large' (480x640dp VGA и WVGA средней плотности); 'xlarge' (720x960dp). Указанные размеры не обязательно должны точно совпадать с реальными размерами устройства, система будет использовать наиболее близкий и подходящий.
  6. Ориентация экрана. 'port' - портретная ориентация; 'land' - альбомная
  7. Плотность пискселей на экране (dpi). Например 'ldpi' (низкая плотность, около 120dpi); 'mdpi' (160dpi); 'hdpi' (240dpi); 'xhdpi' (320dpi); 'nodpi' (для изображений, которые не должны изменяться)
  8. Тач-скрин. 'notouch' (нет тач-скрина); 'stylus' (тач-скрин, который подходит для использования стилуса); 'finger' (тачскрин, который подходит для использования пальцем)
  9. Способ ввода. 'nokeys' у устройства нет клавиатуры; 'qwerty' есть qwerty-клавиатура
  10. Способы навигации кроме тач-скрина. Например 'nonav' не имеется; 'dpad' - джойстик; 'trackball' - трекбол;
  11. Версия платформы (API level). Например 'v3', 'v4', 'v7'
Полный список спецификаторов можно посмотреть в таблице.

При использовании спецификаторов необходимо помнить:
  • Можно использовать несколько спецификаторов в названии одной директории. Например drawable-ru-land-trackball. Причем спецификаторы должны быть в порядке котором они указаны в таблице
  • Директории ресурсов со спецификаторами (как впрочем и без них) не могут быть вложенными.
  • Значение спецификатора не зависят от регистра, во время компиляции все они приводятся к нижнему регистру.
  • В название одной директории может использоваться только один спецификатор каждого типа. Поэтому название drawable-ru-en некорректно, в случае если ресурсы для русского и английских языков совпадают необходимо все равно создавать две директории.
Во время выполнения программы система сначала ищет необходимый ресурс в каталоге специфичном для конкретной конфигурации, а если не находит, то переходит в стандартный каталог (без спецификаторов).

Возьмем для примера программу из предыдущей статьи. При повороте телефона, картинка автоматически поворачивается.
Кстати, если вы запускаете программу на эмляторе, то повернуть экран можно сочетанием клавиш Ctrl+Win+F11.
Теперь сделаем, чтобы при повороте экрана внешний вид немного менялся, скажем, перенесем дату вправо. Для этого надо создать новый xml-файл разметки. До этого все xml-файлы мы клали в директорию res/layout. Создадим директорию res/layout-land откуда будут браться файлы разметки для горизонтальной ориентации экрана. Можно создать отдельную директорию res/layout-port для вертикальной ориентации, но в данном случае этими файлами являются файлы из res/layout, поэтому особо смысла в этом нет.
Для того чтобы изменить внешний вид списка достаточно изменить один файл item.xml, в котором содержится разметка одной строки списка. Скопируем файл в res/layout-land и немного изменим
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_height="wrap_content"   
  4.     android:layout_width="wrap_content"  
  5.     android:padding="5px"  
  6. >  
  7.     <ImageView   
  8.         android:id="@+id/Image"   
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.     />  
  12.               
  13.     <TextView   
  14.         android:id="@+id/Sign"   
  15.         android:layout_width="wrap_content"  
  16.         android:layout_height="wrap_content"  
  17.         android:textSize="20sp"  
  18.         android:textStyle="bold"  
  19.         android:textColor="#FF0000"  
  20.         android:layout_weight="1.0"  
  21.     />  
  22.       
  23.     <TextView    
  24.         android:id="@+id/Date"  
  25.         android:layout_width="wrap_content"   
  26.         android:layout_height="wrap_content"  
  27.         android:textSize="14sp"  
  28.         android:textStyle="italic"  
  29.     />  
  30.   
  31. </LinearLayout>  

Теперь все три элемента располагаются в строку, также мы добавили в TextView (id="@+id/Sign") атрибут android:layout_weight="1.0". Этот атрибут добавляет «вес» элементу в контейнере (layout). Элемент, имеющий «вес» равный 1.0 занимает все свободное пространство в контейнере. Таким образом название знака зодиака займет все свободной пространство, а дата прижмется к правой границе. Все, никаких изменений в коде не потребуется. Запускаем.

Теперь при повороте телефона список изменяет внешний вид.


Если же в приложении поворот экрана нежелателен, можно использовать метод
void setRequestedOrientation(int requestedOrientation)
который задает ориентацию экрана вне зависимости от положения телефона.
Добавим этот метод в onCreate
  1. public void onCreate(Bundle savedInstanceState) {  
  2.         super.onCreate(savedInstanceState);  
  3.         setContentView(R.layout.main);  
  4.           
  5.         setRequestedOrientation (ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);  
  6.         ...  
  7.     }  

Возможные значения переменной requestedOrientation, можно посмотреть тут

Запускаем. Теперь как не крути телефон, список не меняется.


UPD

Создание ссылок на ресурсы

Как было сказано выше, в название одной директории может использоваться только один спецификатор каждого типа. Поэтому название drawable-ru-en некорректно, в случае если ресурсы для русского и английских языков совпадают необходимо создавать две директории. Но это не значит, что придется хранить две копии одного файла.
Например, нам нужно изображение image.png одинаковое для русского и английского языка. Переименуем его и положим в res/drawable/image_ru_en.png. А в двух директориях res/drawable-ru и res/drawable-en создадим файл image.xml, который ссылается на это изображение.
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <bitmap xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:src="@drawable/icon_ru_en" />  
Теперь файл у нас хранится в одном месте, а в двух директориях только ссылки. Из программного кода к этому изображению можно обращаться как R.drawable.image так и R.drawable.image_ru_en

Можно делать ссылки на файлы разметки, используя теги <merge> и <include>
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <merge>  
  3.     <include layout="@layout/main_ru_en"/>  
  4. </merge>  
Совсем просто можно ссылать на строки, используя id нужной нам строки
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.     <string name="string">My String</string>  
  4.     <string name="alias">@string/string</string>  
  5. </resources>  

Комментариев нет:

Отправить комментарий