Урок 5. Layout-файл в Activity. XML представление. Смена ориентации экрана.


Какой layout-файл используется в Activity

На прошлом уроке мы выяснили, что Activity читает layout-файл и отображает то, что в нем сконфигурировано. Теперь выясним, откуда Activity знает, какой именно layout-файл читать. А еще в этом уроке находится подсказка, как зарегиться на нашем форуме ;)

В этом уроке мы будем поворачивать экран. Эмулятор версии 2.3.3, который мы успешно применяли и будем применять для тестирования наших приложений, имеет глюк. Он не хочет корректно поворачивать экран. Поэтому вспомните, плз, урок 3 и создайте еще один эмулятор версии 2.2, например. Соответственно при создании проекта, тоже указываем версию 2.2. Скрины сделаны на версии 2.3.3 - не обращайте внимания на это.

Создадим новый проект:

Project name: P0051_LayoutFiles
Build Target: Android 2.2
Application name: LayoutFiles
Package name: ru.startandroid.develop.LayoutFiles
Create Activity: MainActivity

При разработке каждому Activity сопоставляется одноименный java-класс (наследник класса android.app.Activity). При запуске приложения, когда система должна показать Activity и в дальнейшем работать с ним, она будет вызывать методы этого класса. И от того, что мы в этих методах накодим, зависит поведение Activity.

При создании проекта мы указывали, что надо создать Activity с именем MainActivity

При использовании старого визарда это выглядело так:


В новом визарде - чуть по другому. На одном экране галка, на другом - имя:


Мы попросили создать Activity и Eclipse создал нам соответствующий класс (в дальнейшем мы научимся их создавать самостоятельно).

Давайте посмотрим этот класс: откроем двойным кликом файл src/ru.startandroid.develop.LayoutFiles/MainActivity.java

В нашем классе мы видим, что реализован метод onCreate – он вызывается, когда приложение создает и отображает Activity (на onCreateOptionsMenu пока не обращаем внимания). Посмотрим код реализации.

Первая строка:
super.onCreate(savedInstanceState); – конструктор родительского класса, выполняющий необходимые процедуры, его мы не трогаем.

Нас интересует этот код:
setContentView(R.layout.main);

Метод setContentView(int) – устанавливает содержимое Activity из layout-файла. Но в качестве аргумента мы указываем не путь к нашему layout-файлу (res/layout/main.xml), а константу, которая является ID файла. Это константа генерируется автоматически здесь gen/ru.startandroid.develop.LayoutFiles/R.java.

Этот файл можно открыть и посмотреть, но менять его не стоит. В R классе будут храниться сгенерированные ID для всех ресурсов проекта (из папки res/*), чтобы мы могли к ним обращаться. Имена этих ID-констант совпадают с именами файлов ресурсов (без расширений).

Откроем res/layout/main.xml, посмотрим, что там

Запустим приложение и посмотрим что оно нам покажет

Все верно - Activity отобразил то, что прописано в main.xml.

Попробуем отобразить содержимое другого файла. Создадим еще один layout-файл, например myscreen.xml. Для этого выделим папку res/layout в нашем проекте и нажмем кнопку создания нового файла

Откроется визард


В поле File вводим имя файла: myscreen.xml и жмем Finish.

Новый layout-файл должен сразу открыться. Добавим TextView и через Properties изменим его текст на: «Этот экран описан не в main.xml, а в myscreen.xml». 

При этом Eclipse будет подчеркивать этот текст желтым цветом и ругаться примерно так: [I18N] Hardcoded string "...", should use @string resource. Это он возмущен хардкодом, который мы тут устроили. Он хочет, чтобы использовали файлы ресурсов для всех надписей. Но пока что мы это не умеем, так что игнорируем эти предупреждения.


Обязательно сохраняем. Чтобы в R.java появилась новая константа для этого файла - R.layout.myscreen.

Теперь настроим так, чтобы Activity использовало новый файл myscreen.xml, а не main.xml. Откроем MainActivity.java и поменяем аргумент метода setContentView. Замените «R.layout.main», на «R.layout.myscreen» (ID нового layout-файла). Должно получиться так:

Сохраняем, запускаем приложение. Видим, что теперь оно отображает содержимое из myscreen.xml, т.к. мы явно ему это указали в методе setContentView, который выполняется при создании (onCreate) Activity


Layout-файл в виде XML

Открыв файл main.xml, вы видите его визуальное представление. Т.е. некий предпросмотр, как это будет выглядеть на экране. Снизу вы можете видеть две вкладки – Graphical Layout и main.xml. Откройте main.xml

Мы видим достаточно читабельное xml-описание всех View нашего layout-файла. Названия xml-элементов - это классы View-элементов, xml-атрибуты - это параметры View-элементов, т.е. все те параметры, что мы меняем через вкладку Properties. Также вы можете вносить изменения прямо сюда и изменения будут отображаться в Graphical Layout. Например изменим текст у TextView. Вместо ссылки на константу, вставим свой текст «Какой-то текст»

Сохраняем. Открываем Graphical Layout и наблюдаем изменения.


Обычно авторы учебников дают содержание layout-файлов именно в xml виде. Это удобно – вы можете просто скопировать фрагмент и использовать, и не надо вручную добавлять View-элементы,  бегать по Properties и настраивать все руками. Я буду делать в своих проектах также.


Layout-файл при смене ориентации экрана

По умолчанию мы настраиваем layout-файл под вертикальную ориентацию экрана. Но что будет если мы повернем смартфон и включится горизонтальная ориентация? Давайте смотреть.

Изменим myscreen.xml. Добавим вертикальный ряд кнопок и изменим надпись.

xml-код (вы можете скопировать его и вставить в ваш файл):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Вертикальная ориентация экрана">
</TextView>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/linearLayout1"
android:orientation="vertical">
<Button
android:text="Button1"
android:id="@+id/button1"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button2"
android:id="@+id/button2"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button3"
android:id="@+id/button3"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button4"
android:id="@+id/button4"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
</LinearLayout>
</LinearLayout>

Обратите внимание - я добавил вертикальный LinearLayout и поместил в него 4 кнопки. Подробнее обсудим это на следующем уроке.

Запустим приложение. В вертикальной ориентации все ок.

Нажмем в эмуляторе CTRL+F12, ориентация сменилась на горизонтальную и наши кнопки уже не влезают в экран  (эмулятор версии 2.3.3 глючит и смена ориентации срабатывает не всегда)


Т.е. нам необходим еще один layout-файл, который был бы заточен под горизонтальную ориентацию и в нашем случае вывел бы кнопки горизонтально.

Но как дать знать Activity, что она в вертикальной ориентации должна использовать один layout-файл, а в горизонтальной – другой? Об этом за нас уже подумали создатели Андроид. Необходимо создать папку res/layout-land, а в ней создать layout файл с тем же именем, что и основной. Этот файл будет использован в горизонтальной ориентации.

Создаем папку: правой кнопкой на res, New > Folder, Folder name = layout-land, жмем Finish.

Далее создадим файл res/layout-land/myscreen.xml и настроим его под горизонтальную ориентацию. Аналогично, как и в первый раз, жмем кнопку создания файла. Откроется визард.

Вводим имя файла: myscreen.xml


Визард может ругнуться, что есть уже такой файл - The destination file already exists. Это он углядел ранее созданный файл res/layout/myscreen.xml. Нам надо ему сообщить, что новый файл предназначен для папки res/layout-land, а не res/layout. Жмем Next

А здесь уже руками внизу допишите приставку -land, чтобы получилось res/layout-land


Как вариант, можно было руками не дописывать, а добавить из левого столбца в правый атрибут Orientation и указать ему значение Landscape. Визард все понял бы и сам дописал к пути приставку -land.

А мы сами дописали путь и визард сам добавил атрибут направо. 

Жмем Finish и получаем готовый файл.

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Горизонтальная ориентация экрана">
</TextView>
<LinearLayout
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/linearLayout1"
android:orientation="horizontal">
<Button
android:text="Button1"
android:id="@+id/button1"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button2"
android:id="@+id/button2"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button3"
android:id="@+id/button3"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
<Button
android:text="Button4"
android:id="@+id/button4"
android:layout_height="100dp"
android:layout_width="100dp">
</Button>
</LinearLayout>
</LinearLayout>


Запустим приложение. Activity читает layout-файл, который мы указывали в методе setContentView, т.е. myscreen.xml и отображает его содержимое. Переключим ориентацию CTRL+F12. Activity понимает, что находится в горизонтальной ориентации, ищет в папке layout-land файл myscreen.xml и использует уже его.


В этом уроке мы:

разобрали, откуда Activity знает, какой layout-файл надо читать и настроили его на чтение другого файла
рассмотрели layout-файл с другого ракурса – XML
узнали, какой layout-файл используется при смене ориентации экрана (горизонтальная/вертикальная)

В следующем уроке:

изучим основные виды layout: LinearLayout, TableLayout, RelativeLayout, AbsoluteLayout