Layout файл в activity xml представление смена ориентации экрана
Адаптивный layout — это layout, который предназначен для работы с различными размерами и ориентациями экрана, а также с различными устройствами, на различных языках и версиях Android.
В этом уроке вы узнаете, как создать адаптивный layout путем экстернализации (вынесения в отдельные компоненты) и группировки ресурсов, предоставляя альтернативные ресурсы и ресурсы по умолчанию для вашего приложения.
Экстернализация ресурсов (вынесение данных во внешний файл)
Чтоб экстернализировать ресурсы, нужно хранить их отдельно от кода приложения. Например, вместо того, чтобы жёстко прописывать какую-то строку в коде, вам нужно дать имя строки и добавить её в файл strings.xml , который в Android Studio вы можете найти на панели Project > Android в папке res > values.
Во внешние файлы также необходимо выносить такие ресурсы, как иконки, layouts, drawables (графические объекты). Это позволит:
- Поддерживать внешние ресурсы отдельно от остальной части кода. Если необходимо изменить ресурс, который используется в коде в нескольких местах, это можно сделать единожды в одном месте.
- Предоставить альтернативные ресурсы, которые будут поддерживать определённые конфигурации устройств (например, устройства с различными языками или разными размерами экрана).
Группировка ресурсов
Сохраняйте все ресурсы в папке res. Сортируйте их по типу в отдельные папки внутри res. Для этих папок нужно использовать стандартизированные имена.
Например, на скриншоте ниже показана иерархия файлов для небольшого проекта, которую можно увидеть на панели Project > Android. Папки, которые содержат ресурсы этого проекта по умолчанию, используют стандартизированные имена: drawable, layout, menu, mipmap (для иконок) и values.
В таблице ниже перечислены основные стандартизированные имена папок ресурсов.
Стандартизированные имена папок ресурсов
- arrays.xml для массивов ресурсов,
- dimensions.xml для значений различных размеров в интерфейсах,
- strings.xml , colors.xml , styles.xml |
Альтернативные ресурсы
Большинство приложений предоставляет альтернативные ресурсы для поддержки определённых конфигураций устройств. Например, в вашем приложении должны быть альтернативные drawable-ресурсы для разных разрешений экрана и альтернативные строковые ресурсы для различных языков. Во время запуска приложения Android определит текущую конфигурацию устройства и загрузит соответствующие ресурсы.
Если для какой-либо конфигурации устройства нет доступных ресурсов, Android воспользуется ресурсами приложения по умолчанию.
Как и ресурсы по умолчанию, альтернативные ресурсы хранятся в папках внутри res. Для этих папок используется следующий шаблон названия:
- resource_name — это имя папки для этого типа ресурса (см. табл. выше) Например, drawable или values.
- config_qualifier определяет конфигурацию устройства, для которого будут использоваться ресурсы. Все возможные квалификаторы рассматриваются здесь (таблица 2).
- Чтобы добавить несколько квалификаторов к одному имени папки, разделите их с помощью тире. Если вы используете несколько квалификаторов для папки ресурса, их нужно расположить в порядке их перечисления в таблице.
Примеры с одним квалификатором:
- Строковые ресурсы, локализованные на японский язык, будут находиться в файле strings.xml внутри папки values-ja в папке res (сокращенно res/values-ja/strings.xml ). Строковые ресурсы по умолчанию (ресурсы, которые должны использоваться, когда не найдено специфичных для языка ресурсов) будут находиться в файле res/values/strings.xml . Обратите внимание, что XML-файлы имеют одинаковые имена, в данном случае — strings.xml .
- Стили компонентов для API уровня 21 и выше будут находиться в файле res/values-v21/styles.xml . Ресурсы стилей по умолчанию находятся в res/values/styles.xml .
Пример с несколькими квалификаторами:
- Layout-ресурсы, предназначенные для расположения элементов справа налево и использования в «ночном» режиме, будут находиться в папке res/layout-ldrtl-night/. Это правильное имя папки, потому что в таблице квалификатор для направления элементов указан до того, как указан квалификатор для ночного режима. Папка с именами квалификаторов в обратном порядке (res/layout-night-ldrtl) будет неправильной.
Создание альтернативных ресурсов
Чтобы создать папки альтернативных ресурсов в Android Studio, выберите Android на панели Project, согласно указателю на рисунке ниже.
Чтобы использовать Android Studio для создания новой папки альтернативных ресурсов для определённой конфигурации в папке res, придерживайтесь следующей инструкции:
- Убедитесь, что вы используете панель Project > Android, как показано выше.
- Щелкните правой кнопкой мыши по папке res и выберите New > Android resource directory. Откроется диалоговое окно создания нового ресурса.
- Выберите необходимые для данного набора альтернативных ресурсов тип и квалификаторы.
- Нажмите ОК.
Сохраните альтернативные ресурсы в новой папке. Обратите внимание, что альтернативные файлы ресурсов должны быть названы точно так же, как файлы ресурсов по умолчанию (например, styles.xml или dimens.xml ).
Основные квалификаторы альтернативных ресурсов
Ориентация экрана
Квалификатор ориентации экрана имеет два возможных значения:
- port: Устройство находится в портретном режиме (вертикальном). Так, res/layout-port/ будет содержать layout-файлы, которые будут использоваться, когда устройство находится в портретном режиме.
- land: Устройство находится в альбомном режиме (горизонтальном). Соответственно, res/layout-land/ будет содержать layout-файлы, которые будут использоваться, когда устройство находится в альбомном режиме.
Если пользователь поворачивает экран во время работы приложения и в приложении есть альтернативные ресурсы для такого случая, Android автоматически перезагружает приложение с использованием альтернативных ресурсов, которые будут соответствовать новой конфигурации устройства.
Чтобы создать layout для альбомной ориентации и больших дисплеев, используйте layout-редактор. Для этого следуйте указаниям ниже:
- Откройте XML-файл (например, activity_main.xml ) в Android Studio. Появится layout-редактор.
- Перейдите на вкладку Design в нижней части редактора макетов (если он еще не выбран).
- Нажмите на кнопку Orientation in Editor на верхней панели инструментов.
- Выберите параметр Create Landscape Variation.
- Для альбомной (горизонтальной) вариации откроется новое окно редактора с файлом land/activity_main.xml . Вы можете изменить этот layout для альбомной ориентации, не изменяя исходную портретную (вертикальной) ориентацию.
- Перейдите в каталог res > layout из панели Project > Android, и вы увидите, что Android Studio автоматически создала для вас вариант с именем activity_main.xml (land) для альбомной версии activity_main.xml .
Версия платформы
Квалификатор версии платформы определяет минимальный уровень API, поддерживаемый устройством. Например, используйте v11 для API уровня 11 (устройства на Android 3.0 или выше).
Для работы изображений WebP требуется уровень API 14 (Android 4.0) или выше, а для их полной поддержки требуется уровень API 17 (Android 4.2) или выше. Если вы используете изображения WebP:
- Поместите версии изображений по умолчанию в папку res/drawable. Эти изображения должны быть формата, поддерживаемого всеми уровнями API, например PNG.
- Поместите версии изображений в формате WebP в папку res/drawable-v17. Если устройство использует API уровня 17 или выше, Android выберет эти ресурсы во время запуска приложения.
Локализация
Квалификатор локализации указывает язык и регион (опционально), который используется в приложении. Данный квалификатор представляет собой двухбуквенный ISO 639-1 код языка, за которым (опять же, опционально) следует двухбуквенный код региона (перед ним будет находиться строчная буква r).
Вы можете указать только язык, но только регион указать нельзя. Примеры:
- res/values-fr-rFR/strings.xml
Строки в этом файле используются на устройствах с французским языком во Франции. - res/mipmap-fr-rCA/strings.xml
Строки из этого файла будут используются на устройствах с французском языке, но уже в Канаде. - res/layout-ja/content_main.xml
Этот layout будет использоваться на устройствах с японским языком.
Если пользователь меняет язык или регион в системных настройках устройства во время работы приложения и в приложении есть соответствующие альтернативные ресурсы, то Android автоматически перезагрузит приложение, используя альтернативные ресурсы, которые соответствуют новой конфигурации устройства.
Ресурсы по умолчанию
Ресурсы по умолчанию определяют дизайн и контент по умолчанию для вашего приложения. Например, когда приложение работает на устройстве с языком, для которого вы не указали специфичный для него текст, Android загрузит строки по умолчанию из файла res/values/strings.xml. Если файл по умолчанию отсутствует или в нём отсутствует хотя бы одна критичная для работы приложения строка, то приложение просто не запустится и выдаст ошибку.
Ресурсы по умолчанию имеют стандартные имена папок ресурсов (например, values) без квалификаторов в названии папки или скобок после названий файлов.
Совет: всегда предоставляйте ресурсы по умолчанию, поскольку очень вероятно, что ваше приложение будет работать с устройствами, конфигурации которых не были предусмотрены.
На прошлом уроке мы выяснили, что 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
Когда создавались первые портативные устройства - КПК и смартфоны, то за основу бралась настольная операционная система и допиливалась под мобильное устройство. Лишние функции удалялись, а некоторые функции добавлялись. Но при этом как-то совсем упустили из виду, что в отличие от громоздких мониторов и экранов ноутбуков, карманные устройства можно вращать в руках. Первые устройства не умели менять ориентацию экрана. Некоторые программисты самостоятельно стали создавать программы, которые умели переключаться в разные режимы. Затем эту возможность стали включать в настройки аппарата. Позже аппараты научились самостоятельно определять ориентацию экрана.
Всего существует два режима - портретный и альбомный. На большинстве телефонов используется по умолчанию портретный режим (как на паспорте). Альбомный режим знаком нам по обычным мониторам.
Рассмотрим следующий случай. Предположим, у нас в приложении имеется одно текстовое поле и шесть кнопок. Вроде всё нормально.
Но стоит нам повернуть устройство на 90 градусов, как сразу обнаруживаются проблемы. Пятая кнопка видна частично, а шестая вообще оказалась за пределами видимости. Непорядок!
Чтобы избежать такой проблемы, необходимо как-то по другому скомпоновать кнопки. Например, расположить их не подряд друг за другом, а разбить на пары. Воспользуемся контейнером TableLayout. С его помощью мы можем разбить кнопки на две колонки и поместить их в три ряда.
Для этой операции нам понадобится сделать несколько важных шагов. Сначала нужно создать новую подпапку в папке res. Выделяем папку res, вызываем из него контекстное меню и последовательно выбираем команды New | Android resource directory. В диалоговом окне из выпадающего списка Resource type: выбираем layout. В списке Available qualifiers: находим элемент Orientation и переносим его в правую часть Chosen qualifiers: с помощью кнопки с двумя стрелками. По умолчанию у вас появится имя папки layout-port в первой строке Directory Name:. Но нам нужен альбомный вариант, поэтому в выпадающем списке Screen orientation выбираем Landscape. Теперь название папки будет layout-land.
Можно обойтись без помощи мастера, создав папку сразу через меню New | Directory. Этот способ годится для опытных разработчиков, которые знают, как следует назвать папку. Важно запомнить, что имя даётся не произвольно, а именно в таком виде layout-land. По суффиксу -land система понимает, что речь идёт о новом режиме. Теперь нам осталось создать в созданной папке новый XML-файл activity_main.xml. Вызываем контекстное меню у папки layout-land и выбираем команды New | Layout Resource File. В диалоговом окне присваиваем имя activity_main.xml, которое должно совпадать с именем существующего файла. Во втором поле вводим LinearLayout, по мере ввода появится подсказка, облегчающая выбор.
Откроем созданный файл и модифицируем его следующим образом.
Пример писался во времена Android 2.3. Сейчас проще создать нужную разметку через ConstraintLayoutЗапускаем приложение и проверяем. Отлично, теперь видны все кнопки. Поздравляю, вы гений!
Когда вы создаёте альтернативную разметку, то не забывайте включать все компоненты, к которым будете обращаться программно, иначе получите ошибку. Допустим, вы забыли добавить шестую кнопку. В портретном режиме программа будет работать, а когда пользователь перевернёт экран, то активность будет инициализировать все компоненты для работы, а кнопки-то и нет. Крах приложения и минусы в отзывах.
Узнать ориентацию программно
Чтобы из кода узнать текущую ориентацию, можно создать следующую функцию:
Вызовите данную функцию из нужного места, например, при щелчке кнопки и узнайте текущую ориентацию. В примере использовались две распространённые системные константы для ориентации. Есть ещё константа ORIENTATION_SQUARE (квадратный экран). Но я таких телефонов не встречал.
Можно также вычислить ширину и высоту экрана, если высота больше ширины, то устройство в портретной ориентации, иначе - в альбомной:
Сейчас этот код считается устаревшим и для вычисления размера экрана используются другие методы (описано в примере Экран).
Кручу-верчу, запутать хочу!
Хорошо, мы можем определить текущую ориентацию, но в какую сторону повернули устройство? Ведь его можно повернуть влево, вправо или вообще вверх тормашками. Напишем другую функцию:
Раньше существовал аналогичный метод getOrientation(), который устарел. Используйте getRotation(). Впрочем, спустя несколько лет и этот код теперь считается устаревшим. Замену не искал. На многих телефонах и эмуляторах код работает частично или не работает или работает неправильно. Точно работает на планшете Google Nexus 7 2013. Не стоит полагаться на код в рабочем приложении.Установить ориентацию программно и через манифест
Если вы большой оригинал и хотите запустить приложение в стиле "вид сбоку", то можете сделать это программно. Разместите код в методе onCreate():
Учтите, что в этом случае котам не очень удобно будет пользоваться вашим приложением.
Вы можете запретить приложению менять ориентацию, если добавите нужный код в onCreate().
Но указанный способ не совсем желателен. Лучше установить нужную ориентацию через манифест, прописав в элементе <activity> параметр android:screenOrientation:
Кстати, существует ещё один вариант, когда устройство полагается на показания сенсора и некоторые другие:
В Android 4.3 (API 18) появились новые значения (оставлю пока без перевода):
- userLandscape - Behaves the same as "sensorLandscape", except if the user disables auto-rotate then it locks in the normal landscape orientation and will not flip.
- userPortrait - Behaves the same as "sensorPortrait", except if the user disables auto-rotate then it locks in the normal portrait orientation and will not flip.
- fullUser - Behaves the same as "fullSensor" and allows rotation in all four directions, except if the user disables auto-rotate then it locks in the user's preferred orientation.
- locked - to lock your app's orientation into the screen's current orientation.
После появления Android 5.0 зашёл на страницу документации и пришёл в ужас. Там появились новые значения.
Запрет на создание новой активности
На примере программной установки ориентации можно увидеть интересный эффект, о котором нужно помнить. Предположим у нас есть кнопка, позволяющая менять ориентацию. Заодно будем менять текст на кнопке, чтобы операция соответствовала надписи.
Теперь посмотрите, что у нас получилось. Запустите проект и нажмите на кнопку. Ориентация экрана поменялась, однако текст на кнопке остался прежним, хотя по нашей задумке он должен измениться.
Нажмём на кнопку ещё раз. Надпись изменится, но ориентация не сменится. И только повторный щелчок повернёт экран в обратную сторону.
По умолчанию, при смене ориентации Android уничтожает и пересоздаёт активность из кода, что подразумевает повторный вызов метода onCreate(). Поэтому при повороте активность устанавливала текст, определенный в onCreate(). В большинстве случаев это не мешает программе. Но если приложение воспроизводит видео, то при смене ориентации вызов onCreate() может привести к повторному началу воспроизведения (если так написан пример).
Чтобы активность не пересоздавалась, добавьте в манифест строчку для нужной активности:
Во многих примерах, которые я видел, используется пара keyboardHidden|orientation, но, похоже, в Android 4 этого недостаточно и нужно добавить ещё один атрибут screenSizeПри изменении ориентации система вызовет метод onConfigurationChanged(Configuration) и мы можем отловить поворот экрана:
В документации говорится, что данный способ следует избегать.
Исчезающий текст
Как уже говорилось, при смене ориентации активность пересоздаётся. При этом можно наблюдать интересный эффект с пропадающим текстом. Чтобы увидеть эффект, создадим два текстовых поля. Одному из них присвоим идентификатор, а другое поле оставим без него.
Запустите приложение, введите любой текст в обоих полях и смените ориентацию. Вы увидите, что у поля с идентификатором текст при повороте сохранится, а у поля без идентификатора текст исчезнет. Учитывайте данное обстоятельство.
К вышесказанному могу добавить, что при смене ориентации у поля с идентификатором вызывается метод onTextChanged():
Проверка на существование
Если вы используете две разные разметки, то возможна ситуация, когда в альбомной ориентации используется кнопка, которой нет в портретной ориентации. Это можете привести к ошибке в коде, поэтому нужно проверить существование кнопки:
На практике такое встречается редко, но помните на всякий случай.
Запоминаем значения переменных
С поворотом экрана возникает одна очень неприятная проблема. Вдумайтесь в значение слов, что при повороте экрана активность создаётся заново. Чтобы было понятно, нужно вернуться к проекту, в котором мы считали ворон. Если вы его удалили, то придётся пройти урок заново и восстановить его.
Щёлкните несколько раз по кнопке. Допустим на экране красуется надпись "Я насчитал 5 ворон". Поворачиваем экран - куда улетели все вороны? На экране появилась надпись, что . Впрочем, я не буду говорить вам, сами посмотрите.
А что собственно произошло? Я же вас предупреждал, что активность при повороте создаётся заново. А значит переменная mCount снова принимает значение 0, т.е сбрасывается в начальное значение.
Что же делать? Для этих целей у активности существует специальный метод onSaveInstanceState(), который вызывается системой перед методами onPause(), onStop() и onDestroy(). Метод позволяет сохранить значения простых типов в объекте Bundle. Класс Bundle - это простой способ хранения данных ключ/значение.
Создадим ключ с именем KEY_COUNT. В Android Studio c версии 1.5 появились живые шаблоны, позволяющие быстро создать ключ. Введите до метода onCreate() строчными буквами слово key, во время набора появится подсказка. Нажимаем Enter и получаем заготовку. После символа подчёркивания вводим название ключа. В результате получим ключ следующего вида.
Далее создаём метод onSaveInstanceState() после метода onCreate(). Во время набора имени метода подсказка покажет, что имеется два метода. Выбирайте метод с одним параметров (обычно он идёт вторым). Записываем в ключа значение счётчика.
А в методе onCreate() делаем небольшую проверку.
У метода в параметре содержится объект Bundle. Только здесь он назван savedInstanceState вместо outState, но пусть вас это не вводит заблуждение. Имена вы можете придумывать сами. Главное, что объект содержит сохранённое значение переменной при повороте. При первом запуске приложения объект не существует (null), а потом мы его создали своим кодом. Для этого и нужна проверка. Обратите внимание, что здесь мы не прибавляем единицу к счётчику, как у кнопки. Если скопировать код у кнопки, то получится, что счётчик будет увеличиваться самостоятельно при поворотах без нажатия на кнопку. Прикольно, конечно, но может ввести в заблуждение пользователя. Хотя, если вы пишите приложение "Я твой дом труба шатал", то такой способ может пригодиться для подсчёта, сколько раз вы вертели телефон, чтобы разрушить чей-то дом.
Обращаю ваше внимание, что данный способ используется для сохранения промежуточных результатов во время действия программы. В следующих уроках вы узнаете, как можно сохранять результат между запусками приложения.
Ориентация у фрагментов
Позже вы узнаете о существовании фрагментов. Может возникнуть такая ситуация, когда вы захотите выводить конкретный фрагмент в нужной ориентации. У фрагментов есть собственный жизненный цикл, и вы можете реализовать свой код в методах фрагмента:
Я с таким случаем не встречался, но оставлю как памятку.
Жизненный цикл при повороте
При повороте активность проходит через цепочку различных состояний. Порядок следующий.
При разработке мобильных приложений со сложным дизайном нередки ситуации, когда при изменении ориентации экрана все выглядит совсем не так, как Вам хотелось бы. В таких случаях выходом из ситуации может стать использование различных шаблонов для книжной и альбомной ориентации устройства. При этом переключение между шаблонами может осуществляться в автоматическом или динамическом (ручном) режиме.
Первый способ очень прост. Вы создаете различные версии файла шаблонов для случая альбомной и книжной ориентации и помещаете их в папки res/layout-land для книжной и res/layout-port для портретной ориентации.
Как поменять ориентацию экрана вручную
Если вы хотите управлять сменой шаблона вручную, Вам необходимо воспользоваться методами, предоставляемыми классом Configuration. Объект этого класса передается в качестве параметра в метод onConfigurationChanged, который вызывается при изменении одного из параметров, определенного атрибутами Activity в файле AndroidManifest.xml. Чтобы перехватывать смену ориентации Вам нужно задать атрибут android:configChanges в знаение orientation в файле манифеста. Если этого не сделать, то метод onConfigurationChanged не будет вызываться при смене ориентации устройства.
Блокировка ориентации экрана
Иногда нам требуется запретить смену ориентации. Например, для длинного списка предпочтительной является книжная ориентация, поскольку в этом случае на экране уместится большее количество информации. Вы можете ограничить ориентацию эерана для своей activity с помощью атрибута android:screenOrientation. Этот атрибут можно указать в AndroidManifest.xml. android:screenOrientation="landscape" - альбомная ориентация, android:screenOrientation="portrait" - книжная ориентация. Если Вы ограничиваете свое приложение одной ориентацией, то шаблон должен располагаться в папке res/layout.
Пример android приложения
Давайте создадим проект, в котором продемонстрируем различные способы изменения шаюлона экрана при смене ориентации. Приложение судет состоять из трех кнопок, при нажатии на которые будет открываться activity, в которой будет реализован тот или иной способ работы с ориентацией. Внутри шаблона приложения с помощью атрибута android:onClick объявим методы, которые будут вызываться при нажатии на кнопки.
Шаблон нашего приложения имеет вид:
activity_main.xml
Внутри класса MainActivity определим эти методы.
Реализация автоматической ориентации экрана
Создайте файл шаблона activity_automatic.xml в папке res/layout-land. Этот файл определяет вид экрана при альбомной ориентации.
Аналогичный файл для книжной ориентации создадим в папке res/layout-port. Обратите внимание, что TextView имеет разные цвета.
Для реализации автоматического изменения шаблона создадим класс AutomationOrientation. Внутри класса мы просто связываем с activity xml файл.
Реализация динамической смены шаблона при переориентации экрана
Давайте теперь создадим Activity, внетри которого смена ориентации экрана будет происходить динамически. Создадим в папке layout два файла activity_dynamic_land.xml и activity_dynamic_port.xml, имеющих следующее содержание
Теперь создадим класс DynamicOrientationDetection, в котором будем определять текущую ориентацию экрана и выбор подходящего шаблона. В этом классе мы переопределяем метод onConfigurationChanged. В файл AndroidManifest.xml добавим поле android:configChanges="orientation". Это обеспечит вызов метода onConfigurationChanged при изменении ориентации экрана. Код AndroidManifest.xml приведен в самом конце статьи.
Реализация блокировки автоматической ориентации экрана
Наконец займемся Activity, в которой запрещено реагировать на изменение ориентации устройства. Сделаем так, чтобы в приложении всегда использовалась альбомная ориентация. Зададим в AnroidManifest.xml атрибут android:screenOrientation="landscape" и создадим файл шаблона в папке res/layout
Класс RestrictedOrientation имеет вид
После добавления всех actiity в файл AndroidManifest.xml он должен иметь вид
AnroidManifest.xml
Исходный файл проекта можно скачать из репозиторияgithub/Code4Reference.
Читайте также: