Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Чтобы создать фрагмент, класс должен унаследовать от Android.App.Fragment
, а затем переопределить метод OnCreateView
. OnCreateView
вызывается действием, в котором размещен фрагмент, когда приходит время отображать этот фрагмент на экране, и возвращает View
. Типичный метод OnCreateView
создает View
, расширяя файл макета и присоединяя его к родительскому контейнеру. Характеристики контейнера важны, так как Android будет применять параметры макета родительского элемента к пользовательскому интерфейсу фрагмента. Проиллюстрируем это на примере.
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.Example_Fragment, container, false);
}
Приведенный выше код расширит представление Resource.Layout.Example_Fragment
и добавит его в качестве дочернего представления в контейнер ViewGroup
.
Примечание.
Подклассы фрагмента должны иметь открытый конструктор без аргументов по умолчанию.
Добавление фрагмента в действие
Существует два способа разместить фрагмент внутри действия:
Декларативно — фрагменты можно использовать декларативно в
.axml
файлах макета с помощью тега<Fragment>
.Программным образом — фрагменты также можно динамически создавать экземпляры с помощью
FragmentManager
API класса.
Программное использование через класс FragmentManager
будет обсуждаться в этом руководстве позднее.
Декларативное использование фрагмента
Чтобы добавить фрагмент через макет, следует использовать тег <fragment>
и указать нужный фрагмент по его атрибуту class
или android:name
. Следующий фрагмент кода демонстрирует, как с помощью атрибута class
объявить fragment
.
<?xml version="1.0" encoding="utf-8"?>
<fragment class="com.xamarin.sample.fragments.TitlesFragment"
android:id="@+id/titles_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
В следующем фрагменте кода показано, как объявить fragment
с помощью атрибута android:name
, который идентифицирует класс Fragment.
<?xml version="1.0" encoding="utf-8"?>
<fragment android:name="com.xamarin.sample.fragments.TitlesFragment"
android:id="@+id/titles_fragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Во время создания действия Android создает экземпляр каждого фрагмента, указанного в файле макета, и добавляет вместо элемента Fragment
представление, которое создается из OnCreateView
.
Фрагменты, добавленные в действие декларативно, являются статическими и сохраняются в действии до его уничтожения. Вы не сможете динамически заменить или удалить фрагмент в период существования действия, к которому он прикреплен.
Каждому фрагменту должен быть назначен уникальный идентификатор:
android:id — как и другие элементы пользовательского интерфейса в файле макета, это уникальный идентификатор.
android:tag — это уникальная строка.
Если не используется ни один из указанных выше методов, фрагмент принимает идентификатор представления контейнера. В следующем примере не назначен ни идентификатор android:id
, ни идентификатор android:tag
, поэтому Android назначает фрагменту идентификатор fragment_container
.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="+@id/fragment_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment class="com.example.android.apis.app.TitlesFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Регистр имени пакета
Android не позволяет использовать прописные буквы в именах пакетов. Если имя пакета содержит прописную букву, при попытке расширить представление создается исключение. Впрочем, Xamarin.Android менее требователен и допускает прописные буквы в пространстве имен.
Например, оба следующих фрагмента кода будут нормально работать в Xamarin.Android, но второй из них приводит к исключению android.view.InflateException
в приложении Java на чистом Android.
<fragment class="com.example.DetailsFragment" android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="match_parent" />
ИЛИ
<fragment class="Com.Example.DetailsFragment" android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="match_parent" />
Жизненный цикл фрагмента
У фрагментов есть собственный жизненный цикл, который хоть и зависит от жизненного цикла действия, в котором он размещен, но имеет некоторую степень свободы. Например, приостановка действия приводит к приостановке всех связанных с ним фрагментов. Жизненный цикл фрагмента представлен на схеме ниже.
Методы жизненного цикла при создании фрагмента
Следующий список демонстрирует поток обратных вызовов в жизненном цикле фрагмента, которые выполняются при его создании.
OnInflate()
— вызывается при создании фрагмента в составе макета представления. Он может вызываться немедленно после декларативного создания фрагмента из XML-файла макета. Фрагмент еще не связан с действием, но ему из иерархии представлений передаются в качестве параметров значения Activity, Bundle и AttributeSet. Этот метод лучше всего использовать для анализа AttributeSet и сохранения любых атрибутов, которые могут впоследствии потребоваться фрагменту.OnAttach()
— вызывается после того, как фрагмент связан с действием. Это первый метод, который вызывается после готовности фрагмента к использованию. Обычно фрагмент не должен реализовывать конструктор или переопределять конструктор по умолчанию. Любые компоненты, которые нужны для работы фрагмента, следует инициализировать в этом методе.OnCreate()
— вызывается действием для создания фрагмента. В момент вызова этого метода иерархия представлений того действия, в котором размещается фрагмент, может быть готова еще не полностью, поэтому фрагмент не может полагаться на любые сегменты этой иерархии раньше следующих этапов жизненного цикла фрагмента. Например, не используйте этот метод для оптимизации или настройки пользовательского интерфейса приложения. Это самый ранний момент, когда фрагмент может начать сбор нужной информации. Фрагмент на этом этапе выполняется в потоке пользовательского интерфейса, поэтому не выполняйте здесь никаких длительных вычислений, а при необходимости выносите их в фоновый поток. Этот метод может быть пропущен, если вызывается SetRetainInstance(true). Этот вариант будет рассмотрен подробнее ниже.OnCreateView()
— создает представление для фрагмента. Этот метод вызывается в тот момент, когда завершается метод OnCreate() соответствующего действия. В этот момент уже можно безопасно взаимодействовать с иерархией представлений действия. Этот метод должен возвращать представление, которое будет использоваться этим фрагментом.OnActivityCreated()
— Вызывается после завершения действия Activity.OnCreate действием размещения. На этом этапе следует выполнять окончательную оптимизацию пользовательского интерфейса.OnStart()
— вызывается после возобновления содержащего действия. С этого момента фрагмент становится виден пользователю. Во многих случаях фрагменты содержат код, который мог бы находиться методе OnStart() соответствующего действия.OnResume()
— Это последний метод, который вызывается, прежде чем пользователь сможет взаимодействовать с фрагментом. В качестве примера кода, который можно выполнять в этом методе, можно предложить включение функций устройства, с которым может взаимодействовать пользователь, например камеры или системы позиционирования. Службы такого рода иногда требуют значительной энергии, поэтому приложению следует минимизировать их использование для продления времени работы от батареи.
Методы жизненного цикла при уничтожении фрагмента
В следующем списке описаны методы жизненного цикла, которые вызываются по мере уничтожения фрагмента.
OnPause()
— пользователь больше не может взаимодействовать с фрагментом. Эта ситуация возникает, если операция в другом фрагменте изменяет этот фрагмент или если действие, в котором размещается фрагмент, приостановлено. Вполне возможно, что действие с этим фрагментом еще отображается на экране, то есть оно может быть полупрозрачным или занимать не весь экран. Активация этого метода является первым признаком того, что пользователь покидает этот фрагмент. Здесь фрагменту следует сохранить все нужные изменения.OnStop()
— Фрагмент больше не отображается. Возможно, остановилась работа соответствующего действия или операция фрагмента изменяет его состояние в действии. Этот обратный вызов имеет такое же назначение, как и Activity.OnStop.OnDestroyView()
— этот метод вызывается для очистки ресурсов, связанных с представлением. Он вызывается при уничтожении представления, с которым связан этот фрагмент.OnDestroy()
— этот метод вызывается, когда фрагмент больше не используется. Он все еще связан с действием, но уже не функционирует. В этом методе следует очистить все ресурсы, которые использует фрагмент, например используемый камерой ресурс SurfaceView. Этот метод может быть пропущен, если вызывается SetRetainInstance(true). Этот вариант будет рассмотрен подробнее ниже.OnDetach()
— Этот метод вызывается непосредственно перед тем, как фрагмент больше не связан с действием. В этот момент иерархия фрагмента уже не существует, и сейчас самое время освободить все ресурсы, которые использует фрагмент.
Использование SetRetainInstance
Фрагмент может указать, что его не нужно уничтожать полностью, если действие будет создано повторно. Для этой цели класс Fragment
предоставляет метод SetRetainInstance
. Если этому методу передать аргумент true
, при восстановлении действия в нем будет размещен тот же экземпляр этого фрагмента. В таком случае будут вызваны все методы обратного вызова из жизненного цикла фрагмента, кроме OnCreate
и OnDestroy
. Этот процесс иллюстрируется на схеме жизненного цикла выше (обозначен зеленым пунктиром).
Управление состоянием фрагмента
Фрагменты могут сохранять и восстанавливать свое состояние в течение своего жизненного цикла, используя экземпляр Bundle
. Bundle позволяет фрагменту сохранять данные как пары "ключ — значение", что удобно для хранения несложных, для которых не требуется много памяти. Фрагмент может сохранить состояние, вызвав OnSaveInstanceState
.
public override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutInt("current_choice", _currentCheckPosition);
}
При создании нового экземпляра фрагмента состояние, сохраненное в Bundle
, становится доступным новому экземпляру через методы OnCreate
, OnCreateView
и OnActivityCreated
нового экземпляра.
В следующем примере демонстрируется извлечение значения current_choice
из Bundle
.
public override void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
if (savedInstanceState != null)
{
_currentCheckPosition = savedInstanceState.GetInt("current_choice", 0);
}
}
Переопределение OnSaveInstanceState
считается правильным приемом для сохранения временных данных фрагмента при изменениях ориентации, таких как значение current_choice
из примера выше. Однако стандартная реализация OnSaveInstanceState
самостоятельно управляет сохранением временных данных пользовательского интерфейса для каждого представления, которому присвоен идентификатор. Вот пример приложения, в котором XML определяет элемент EditText
:
<EditText android:id="@+id/myText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Поскольку элементу управления EditText
назначен идентификатор id
, этот фрагмент автоматически сохраняет данные в мини-приложении при вызове OnSaveInstanceState
.
Ограничения при использовании Bundle
Несмотря на простоту сохранения временных данных с помощью OnSaveInstanceState
, этот метод имеет ряд ограничений.
Если фрагмент не добавлен в обратный стек, состояние фрагмента не будет восстановлено при нажатии пользователем кнопки Назад.
Если для сохранения данных используется Bundle, эти данные сериализуются. Это может приводить к задержкам в обработке.
Участие в работе меню
Фрагменты могут добавлять пункты в меню действия, в котором они размещаются. Действие всегда вначале обрабатывает пункты меню. Если у действия нет соответствующего обработчика, событие передается фрагменту, который его затем обработает.
Чтобы добавить пункты в меню действия, фрагмент должен выполнить два условия.
Во-первых, фрагмент должен реализовать метод OnCreateOptionsMenu
и разместить пункты в меню, как показано в примере кода ниже.
public override void OnCreateOptionsMenu(IMenu menu, MenuInflater menuInflater)
{
menuInflater.Inflate(Resource.Menu.menu_fragment_vehicle_list, menu);
base.OnCreateOptionsMenu(menu, menuInflater);
}
Меню в предыдущем фрагменте кода расширяется из представленного ниже кода XML, который хранится в файле menu_fragment_vehicle_list.xml
.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/add_vehicle"
android:icon="@drawable/ic_menu_add_data"
android:title="@string/add_vehicle" />
</menu>
Затем фрагмент должен вызывать SetHasOptionsMenu(true)
. Вызов этого метода сообщает платформе Android, что фрагмент намерен добавить пункты меню в существующее меню параметров. Если не вызвать этот метод, пункты меню из фрагмента не добавляются в меню параметров соответствующего действия. Обычно этот процесс выполняется в методе жизненного цикла OnCreate()
, как показано в фрагменте кода ниже.
public override void OnCreate(Bundle savedState)
{
base.OnCreate(savedState);
SetHasOptionsMenu(true);
}
На следующем снимке экрана представлено, как будет выглядеть созданное меню.