Обработка поворота
В этом разделе описывается обработка изменений ориентации устройства в Xamarin.Android. В нем описывается, как работать с системой ресурсов Android для автоматической загрузки ресурсов для определенной ориентации устройства, а также как программно обрабатывать изменения ориентации.
Обзор
Так как мобильные устройства легко поворачиваются, встроенная смена является стандартной функцией в мобильных операционных устройствах. Android предоставляет сложную платформу для работы с поворотом в приложениях, независимо от того, создается ли пользовательский интерфейс декларативно в XML или программно в коде. При автоматической обработке декларативных изменений макета на поворачиваемом устройстве приложение может воспользоваться тесной интеграцией с системой ресурсов Android. Для программного макета изменения должны обрабатываться вручную. Это позволяет более точно управлять средой выполнения, но за счет большей работы для разработчика. Приложение также может отказаться от перезапуска действия и управлять изменениями ориентации вручную.
В этом руководстве рассматриваются следующие разделы ориентации:
Декларативная смена макета — как использовать систему ресурсов Android для создания приложений с поддержкой ориентации, включая загрузку макетов и рисуемых элементов для определенных ориентаций.
Смена программного макета— как добавлять элементы управления программным способом, а также как обрабатывать изменения ориентации вручную.
Обработка поворота декларативно с помощью макетов
Включив файлы в папки, которые соответствуют соглашениям об именовании, Android автоматически загружает соответствующие файлы при изменении ориентации. Добавлена поддержка:
Ресурсы макета— указание файлов макета для каждой ориентации.
Ресурсы, доступные для рисования, — указание, какие рисуемые модули загружаются для каждой ориентации.
Ресурсы макета
По умолчанию файлы ANDROID XML (AXML), включенные в папку Resources/layout , используются для просмотра представлений для действия. Ресурсы этой папки используются для книжной и альбомной ориентации, если дополнительные ресурсы макета не предоставляются специально для альбомной ориентации. Рассмотрим структуру проекта, созданную по умолчанию шаблоном проекта:
Этот проект создает один файл Main.axml в папке Resources/layout . При вызове метода Activity OnCreate
он увеличивает представление, определенное в Main.axml, которое объявляет кнопку, как показано в 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/myButton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"/>
</LinearLayout>
Если устройство поворачивается на альбомную ориентацию, метод действия OnCreate
вызывается снова и тот же файл Main.axml раздувается, как показано на снимке экрана ниже:
Макеты, относящиеся к ориентации
Помимо папки макета (которая по умолчанию используется для портрета и может быть явно именованным портом макета, включая именованную layout-land
папку), приложение может определить представления, необходимые в альбомной среде без каких-либо изменений кода.
Предположим, что файл Main.axml содержит следующий XML-файл:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="This is portrait"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</RelativeLayout>
Если папка с именем layout-land, содержащая дополнительный файл Main.axml , добавляется в проект, то при добавлении макета в альбомном режиме будет добавлен новый файл Main.axml в Android. Рассмотрим альбомную версию файла Main.axml , содержащего следующий код (для простоты этот XML аналогичен стандартной книжной версии кода, но использует другую строку в TextView
):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:text="This is landscape"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
</RelativeLayout>
Выполнение этого кода и поворот устройства с книжной на альбомную демонстрирует новую загрузку XML, как показано ниже:
Ресурсы, доступные для рисования
Во время смены Android обрабатывает доступные для рисования ресурсы аналогично макету ресурсов. В этом случае система получает рисуемые элементы из папок "Ресурсы", "Рисуемые " и "Ресурсы"/ "Рисуемые " соответственно.
Например, предположим, что проект содержит изображение с именем Monkey.png в папке Resources/drawable , где на рисуемый объект ссылается из ImageView
XML, как показано ниже:
<ImageView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/monkey"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
Предположим, что другая версия Monkey.png включена в раздел "Ресурсы/рисуемые земли". Как и в файлах макета, когда устройство поворачивается, меняется нарисованное изменение заданной ориентации, как показано ниже:
Обработка поворота программным способом
Иногда мы определяем макеты в коде. Это может произойти по различным причинам, включая технические ограничения, предпочтения разработчика и т. д. При программном добавлении элементов управления приложение должно вручную учитывать ориентацию устройства, которая обрабатывается автоматически при использовании XML-ресурсов.
Добавление элементов управления в код
Чтобы добавить элементы управления программным способом, приложению необходимо выполнить следующие действия:
- Создание макета.
- Задайте параметры макета.
- Создание элементов управления.
- Задайте параметры макета элемента управления.
- Добавьте элементы управления в макет.
- Задайте макет в качестве представления содержимого.
Например, рассмотрим пользовательский интерфейс, состоящий из одного TextView
элемента управления, добавленного в объект RelativeLayout
, как показано в следующем коде.
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// create a layout
var rl = new RelativeLayout (this);
// set layout parameters
var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
rl.LayoutParameters = layoutParams;
// create TextView control
var tv = new TextView (this);
// set TextView's LayoutParameters
tv.LayoutParameters = layoutParams;
tv.Text = "Programmatic layout";
// add TextView to the layout
rl.AddView (tv);
// set the layout as the content view
SetContentView (rl);
}
Этот код создает экземпляр RelativeLayout
класса и задает его LayoutParameters
свойство. Класс LayoutParams
— это способ инкапсулирования того, как элементы управления размещаются повторно. После создания экземпляра макета элементы управления можно создавать и добавлять в него. Элементы управления также имеют такие LayoutParameters
элементы управления, как TextView
в этом примере. TextView
После создания добавьте его в RelativeLayout
представление содержимого и задав RelativeLayout
его в качестве результата в приложении, отображая TextView
его, как показано ниже:
Обнаружение ориентации в коде
Если приложение пытается загрузить другой пользовательский интерфейс для каждой ориентации при OnCreate
вызове (это происходит при каждом повороте устройства), оно должно обнаружить ориентацию, а затем загрузить нужный код пользовательского интерфейса. Android имеет класс с именем WindowManager
, который можно использовать для определения текущего поворота устройства с помощью WindowManager.DefaultDisplay.Rotation
свойства, как показано ниже:
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// create a layout
var rl = new RelativeLayout (this);
// set layout parameters
var layoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.FillParent);
rl.LayoutParameters = layoutParams;
// get the initial orientation
var surfaceOrientation = WindowManager.DefaultDisplay.Rotation;
// create layout based upon orientation
RelativeLayout.LayoutParams tvLayoutParams;
if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
} else {
tvLayoutParams = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
tvLayoutParams.LeftMargin = 100;
tvLayoutParams.TopMargin = 100;
}
// create TextView control
var tv = new TextView (this);
tv.LayoutParameters = tvLayoutParams;
tv.Text = "Programmatic layout";
// add TextView to the layout
rl.AddView (tv);
// set the layout as the content view
SetContentView (rl);
}
Этот код задает TextView
расположение 100 пикселей в левом верхнем углу экрана, автоматически анимируя новый макет, при повороте в альбом, как показано ниже:
Предотвращение перезапуска действия
Помимо обработки всего в OnCreate
приложении, приложение также может предотвратить перезапуск действия при изменении ориентации, установив ConfigurationChanges
в ActivityAttribute
следующем виде:
[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
Теперь, когда устройство поворачивается, действие не перезапускается. Чтобы вручную обработать изменение ориентации в этом случае, действие может переопределить OnConfigurationChanged
метод и определить ориентацию от Configuration
объекта, переданного, как в новой реализации действия ниже:
[Activity (Label = "CodeLayoutActivity", ConfigurationChanges=Android.Content.PM.ConfigChanges.Orientation | Android.Content.PM.ConfigChanges.ScreenSize)]
public class CodeLayoutActivity : Activity
{
TextView _tv;
RelativeLayout.LayoutParams _layoutParamsPortrait;
RelativeLayout.LayoutParams _layoutParamsLandscape;
protected override void OnCreate (Bundle bundle)
{
// create a layout
// set layout parameters
// get the initial orientation
// create portrait and landscape layout for the TextView
_layoutParamsPortrait = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
_layoutParamsLandscape = new RelativeLayout.LayoutParams (ViewGroup.LayoutParams.FillParent, ViewGroup.LayoutParams.WrapContent);
_layoutParamsLandscape.LeftMargin = 100;
_layoutParamsLandscape.TopMargin = 100;
_tv = new TextView (this);
if (surfaceOrientation == SurfaceOrientation.Rotation0 || surfaceOrientation == SurfaceOrientation.Rotation180) {
_tv.LayoutParameters = _layoutParamsPortrait;
} else {
_tv.LayoutParameters = _layoutParamsLandscape;
}
_tv.Text = "Programmatic layout";
rl.AddView (_tv);
SetContentView (rl);
}
public override void OnConfigurationChanged (Android.Content.Res.Configuration newConfig)
{
base.OnConfigurationChanged (newConfig);
if (newConfig.Orientation == Android.Content.Res.Orientation.Portrait) {
_tv.LayoutParameters = _layoutParamsPortrait;
_tv.Text = "Changed to portrait";
} else if (newConfig.Orientation == Android.Content.Res.Orientation.Landscape) {
_tv.LayoutParameters = _layoutParamsLandscape;
_tv.Text = "Changed to landscape";
}
}
}
TextView's
Здесь параметры макета инициализированы как для альбомной, так и книжной. Переменные класса содержат параметры вместе с TextView
самим собой, так как действие не будет повторно создано при изменении ориентации. Код по-прежнему используется surfaceOrientartion
для OnCreate
задания начального макета для TextView
. После этого OnConfigurationChanged
обрабатывает все последующие изменения макета.
При запуске приложения Android загружает изменения пользовательского интерфейса при смене устройства и не перезапускает действие.
Предотвращение перезапуска действия для декларативных макетов
Перезапуски действий, вызванные поворотом устройства, также могут быть запрещены, если мы определим макет в XML. Например, мы можем использовать этот подход, если мы хотим предотвратить перезапуск действия (по соображениям производительности, возможно), и нам не нужно загружать новые ресурсы для разных ориентаций.
Для этого мы следуйте той же процедуре, которую мы используем с программным макетом. Просто установить ConfigurationChanges
в ActivityAttribute
, как мы сделали в предыдущих CodeLayoutActivity
. Любой код, который требуется запустить для изменения ориентации, можно снова реализовать в методе OnConfigurationChanged
.
Сохранение состояния во время изменений ориентации
Независимо от того, следует ли обрабатывать поворот декларативно или программно, все приложения Android должны реализовать те же методы управления состоянием при изменении ориентации устройства. Управление состоянием важно, так как система перезапускает запущенное действие при смене устройства Android. Android делает это, чтобы упростить загрузку альтернативных ресурсов, таких как макеты и рисуемые элементы, разработанные специально для определенной ориентации. При перезапуске действие теряет любое временное состояние, которое может храниться в переменных локального класса. Таким образом, если действие зависит от состояния, оно должно сохранить свое состояние на уровне приложения. Приложение должно обрабатывать сохранение и восстановление любого состояния приложения, которое он хочет сохранить во время изменений ориентации.
Дополнительные сведения о сохранении состояния в Android см. в руководстве по жизненному циклу действий.
Итоги
В этой статье описано, как использовать встроенные возможности Android для работы с поворотом. Во-первых, он объяснил, как использовать систему ресурсов Android для создания приложений с учетом ориентации. Затем он представил, как добавить элементы управления в код, а также как обрабатывать изменения ориентации вручную.