page.title=Фрагменты parent.title=Операции parent.link=activities.html @jd:body
Фрагмент (класс {@link android.app.Fragment}) представляет поведение или часть пользовательского интерфейса в операции (класс {@link android.app.Activity}). Разработчик может объединить несколько фрагментов в одну операцию для построения многопанельного пользовательского интерфейса и повторного использования фрагмента в нескольких операциях. Фрагмент можно рассматривать как модульную часть операции. Такая часть имеет свой жизненный цикл и самостоятельно обрабатывает события ввода. Кроме того, ее можно добавить или удалить непосредственно во время выполнения операции. Это нечто вроде вложенной операции, которую можно многократно использовать в различных операциях.
Фрагмент всегда должен быть встроен в операцию, и на его жизненный цикл напрямую влияет жизненный цикл операции. Например, когда операция приостановлена, в том же состоянии находятся и все фрагменты внутри нее, а когда операция уничтожается, уничтожаются и все фрагменты. Однако пока операция выполняется (это соответствует состоянию возобновлена жизненного цикла), можно манипулировать каждым фрагментом независимо, например добавлять или удалять их. Когда разработчик выполняет такие транзакции с фрагментами, он может также добавить их в стек переходов назад, которым управляет операция. Каждый элемент стека переходов назад в операции является записью выполненной транзакции с фрагментом. Стек переходов назад позволяет пользователю обратить транзакцию с фрагментом (выполнить навигацию в обратном направлении), нажимая кнопку Назад.
Когда фрагмент добавлен как часть макета операции, он находится в объекте {@link android.view.ViewGroup} внутри иерархии представлений операции и определяет собственный макет представлений. Разработчик может вставить фрагмент в макет операции двумя способами. Для этого следует объявить фрагмент в файле макета операции как элемент {@code <fragment>} или добавить его в существующий объект{@link android.view.ViewGroup} в коде приложения. Впрочем, фрагмент не обязан быть частью макета операции. Можно использовать фрагмент без интерфейса в качестве невидимого рабочего потока операции.
В этом документе показано, как построить приложение, использующее фрагменты. В частности, обсуждается, как фрагменты могут поддерживать свое состояние, когда они добавляются в стек переходов назад операции, использовать события совместно с операцией и другими фрагментами внутри нее, выводить данные в строку действий операции и т. д.
Фрагменты впервые появились в Android версии 3.0 (API уровня 11), главным образом, для обеспечения большей динамичности и гибкости пользовательских интерфейсов на больших экранах, например, у планшетов. Поскольку экраны планшетов гораздо больше, чем у смартфонов, они предоставляют больше возможностей для объединения и перестановки компонентов пользовательского интерфейса. Фрагменты позволяют делать это, избавляя разработчика от необходимости управлять сложными изменениями в иерархии представлений. Разбивая макет операции на фрагменты, разработчик получает возможность модифицировать внешний вид операции в ходе выполнения и сохранять эти изменения в стеке переходов назад, которым управляет операция.
Например, новостное приложение может использовать один фрагмент для показа списка статей слева, а другой—для отображения статьи справа. Оба фрагмента отображаются за одну операцию рядом друг с другом, и каждый имеет собственный набор методов обратного вызова жизненного цикла и управляет собственными событиями пользовательского ввода. Таким образом, вместо применения одной операции для выбора статьи, а другой — для чтения статей, пользователь может выбрать статью и читать ее в рамках одной операции, как на планшете, изображенном на рисунке 1.
Следует разрабатывать каждый фрагмент как модульный и повторно используемый компонент операции. Поскольку каждый фрагмент определяет собственный макет и собственное поведение со своими обратными вызовами жизненного цикла, разработчик может включить один фрагмент в несколько операций. Поэтому он должен предусмотреть повторное использование фрагмента и не допускать, чтобы один фрагмент непосредственно манипулировал другим. Это особенно важно, потому что модульность фрагментов позволяет изменять их сочетания в соответствии с различными размерами экранов. Если приложение должно работать и на планшетах, и на смартфонах, можно повторно использовать фрагменты в различных конфигурациях макета, чтобы оптимизировать взаимодействие с пользователем в зависимости от доступного размера экрана. Например, на смартфоне может возникнуть необходимость в разделении фрагментов для предоставления однопанельного пользовательского интерфейса, если разработчику не удается поместить более одного фрагмента в одну операцию.
Вернемся к примеру с новостным приложением. Оно может иметь два фрагмента, встроенных в Операцию А, когда выполняется на устройстве планшетного формата. В то же время на экране смартфона недостаточно места для обоих фрагментов, и поэтому Операция А включает в себя только фрагмент со списком статей. Когда пользователь выбирает статью, запускается Операция В, содержащая второй фрагмент для чтения статьи. Таким образом, приложение поддерживает как планшеты, так и смартфоны благодаря повторному использованию фрагментов в различных сочетаниях, как показано на рисунке 1.
Подробные сведения относительно разработки приложения с различными сочетаниями фрагментов для различных конфигураций экрана приводятся в руководстве Поддержка планшетов и смартфонов
Для создания фрагмента необходимо создать подкласс класса {@link android.app.Fragment} (или его существующего подкласса). Класс {@link android.app.Fragment} имеет код, во многом схожий с кодом {@link android.app.Activity}. Он содержит методы обратного вызова, аналогичные методам операции, такие как {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onStart onStart()}, {@link android.app.Fragment#onPause onPause()} и {@link android.app.Fragment#onStop onStop()}. На практике, если требуется преобразовать существующее приложение Android так, чтобы в нем использовались фрагменты, достаточно просто переместить код из методов обратного вызова операции в соответствующие методы обратного вызова фрагмента.
Как правило, необходимо реализовать следующие методы жизненного цикла:
В большинстве приложений для каждого фрагмента должны быть реализованы, как минимум, эти три метода. Однако существуют и другие методы обратного вызова, которые следует использовать для управления различными этапами жизненного цикла фрагмента. Все методы обратного вызова жизненного цикла подробно обсуждаются в разделе Управление жизненным циклом фрагмента.
Существует также ряд подклассов, которые, возможно, потребуется расширить вместо использования базового класса {@link android.app.Fragment}:
Фрагмент обычно используется как часть пользовательского интерфейса операции, при этом он добавляет в операцию свой макет.
Чтобы создать макет для фрагмента, разработчик должен реализовать метод обратного вызова {@link android.app.Fragment#onCreateView onCreateView()}, который система Android вызывает, когда для фрагмента наступает время отобразить свой макет. Реализация этого метода должна возвращать объект {@link android.view.View}, который является корневым в макете фрагмента.
Примечание. Если фрагмент является подклассом класса {@link android.app.ListFragment}, реализация по умолчанию возвращает класс {@link android.widget.ListView} из метода {@link android.app.Fragment#onCreateView onCreateView()}, так что реализовывать его нет необходиомости.
Чтобы возвратить макет из метода {@link android.app.Fragment#onCreateView onCreateView()}, можно выполнить его раздувание из ресурса макета, определенного в XML-файле. Для этой цели метод {@link android.app.Fragment#onCreateView onCreateView()} предоставляет объект {@link android.view.LayoutInflater}.
Например, код подкласса класса {@link android.app.Fragment}, загружающий макет из файла {@code example_fragment.xml}, может выглядеть так:
public static class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false); } }
В приведенном коде конструкция {@code R.layout.example_fragment} является ссылкой на ресурс макета по имени {@code example_fragment.xml}, хранящийся в ресурсах приложения. Подробные сведения о создании макета в XML см. в статье Пользовательский интерфейс.
Параметр {@code container}, передаваемый методу {@link android.app.Fragment#onCreateView onCreateView()}, является родительским классом {@link android.view.ViewGroup} (из макета операции), в который будет вставлен макет фрагмента. Параметр {@code savedInstanceState} является классом {@link android.os.Bundle}, который предоставляет данные о предыдущем экземпляре фрагмента во время возобновления фрагмента (восстановление состояния подробно обсуждается в разделе Управление жизненным циклом фрагмента).
Метод {@link android.view.LayoutInflater#inflate(int,ViewGroup,boolean) inflate()} принимает три аргумента:
Мы увидели, как создавать фрагмент, предоставляющий макет. Теперь необходимо добавить фрагмент в операцию.
Как правило, фрагмент добавляет часть пользовательского интерфейса в операцию, и этот интерфейс встраивается в общую иерархию представлений операции. Разработчик может добавить фрагмент в макет операции двумя способами:
В этом случае можно указать свойства макета для фрагмента, как будто он является представлением. Например, файл макета операции с двумя фрагментами может выглядеть следующим образом:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment android:name="com.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
Атрибут {@code android:name} в элементе {@code <fragment>} определяет класс {@link android.app.Fragment}, экземпляр которого создается в макете.
Когда система создает этот макет операции, она создает экземпляр каждого фрагмента, определенного в макете, и для каждого вызывает метод {@link android.app.Fragment#onCreateView onCreateView()}, чтобы получить макет каждого фрагмента. Система вставляет объект {@link android.view.View}, возвращенный фрагментом, непосредственно вместо элемента {@code <fragment>}.
Примечание. Каждый фрагмент должен иметь уникальный идентификатор, который система сможет использовать для восстановления фрагмента в случае перезапуска операции. (Что касается разработчика, он может использовать этот идентификатор для захвата фрагмента с целью выполнения транзакций с ним, например, чтобы удалить его). Предоставить идентификатор фрагменту можно тремя способами:
В любой момент выполнения операции разработчик может добавить фрагменты в ее макет. Для этого достаточно указать объект {@link android.view.ViewGroup}, в котором следует разместить фрагмент.
Для выполнения транзакций с фрагментами внутри операции (таких как добавление, удаление или замена фрагмента) необходимо использовать API-интерфейсы из {@link android.app.FragmentTransaction}. Экземпляр класса {@link android.app.FragmentTransaction} можно получить от объекта {@link android.app.Activity} следующим образом:
FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()} FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
После этого можно добавить фрагмент методом {@link android.app.FragmentTransaction#add(int,Fragment) add()}, указав добавляемый фрагмент и представление, в которое он должен быть добавлен. Например:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
Первый аргумент, передаваемый методу {@link android.app.FragmentTransaction#add(int,Fragment) add()}, представляет собой контейнерный объект {@link android.view.ViewGroup} для фрагмента, указанный при помощи идентификатора ресурса. Второй параметр — это фрагмент, который нужно добавить.
Выполнив изменения с помощью {@link android.app.FragmentTransaction}, необходимо вызвать метод {@link android.app.FragmentTransaction#commit}, чтобы они вступили в силу.
Пример, приведенный выше, демонстрирует, как добавлять в операцию фрагмент с предоставлением пользовательского интерфейса. Однако можно использовать фрагмент и для реализации фонового поведения операции без какого-либо дополнительного пользовательского интерфейса.
Чтобы добавить фрагмент без пользовательского интерфейса, добавьте фрагмент из операции, используя метод {@link android.app.FragmentTransaction#add(Fragment,String)} (передав ему уникальный строковый «тег» для фрагмента вместо идентификатора представления). Фрагмент будет добавлен, но, поскольку он не связан с представлением в макете операции, он не будет принимать вызов метода {@link android.app.Fragment#onCreateView onCreateView()}. Поэтому в реализации этого метода нет необходимости.
Передача строкового тега свойственна не только фрагментам без пользовательского интерфейса, поэтому можно передавать строковые теги и фрагментам, имеющим пользовательский интерфейс. Однако, если у фрагмента нет пользовательского интерфейса, то строковый тег является единственным способом его идентификации. Если впоследствии потребуется получить фрагмент от операции, нужно будет вызвать метод {@link android.app.FragmentManager#findFragmentByTag findFragmentByTag()}.
Пример операции, использующей фрагмент в качестве фонового потока, без пользовательского интерфейса, приведен в образце кода {@code
FragmentRetainInstance.java}, входящем в число образцов в SDK (и доступном при помощи
Android SDK Manager). Путь к нему в системе —
<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java
.
Для управления фрагментами в операции нужен класс {@link android.app.FragmentManager}. Чтобы получить его, следует вызвать метод {@link android.app.Activity#getFragmentManager()} из кода операции.
Ниже указаны действия, которые позволяет выполнить{@link android.app.FragmentManager}:
Дополнительные сведения об этих и других методах приводятся в документации по классу {@link android.app.FragmentManager}.
Как было показано в предыдущем разделе, можно использовать класс {@link android.app.FragmentManager} для открытия {@link android.app.FragmentTransaction}, что позволяет выполнять транзакции с фрагментами, например, добавление и удаление.
Большим достоинством использования фрагментов в операции является возможность добавлять, удалять, заменять их и выполнять другие действия с ними в ответ на действия пользователя. Любой набор изменений, вносимых в операцию, называется транзакцией. Ее можно выполнить при помощи API-интерфейсов в {@link android.app.FragmentTransaction}. Каждую транзакцию можно сохранить в стеке переходов назад, которым управляет операция. Это позволит пользователю перемещаться назад по изменениям во фрагментах (аналогично перемещению назад по операциям).
Экземпляр класса {@link android.app.FragmentTransaction} можно получить от {@link android.app.FragmentManager}, например, так:
FragmentManager fragmentManager = {@link android.app.Activity#getFragmentManager()}; FragmentTransaction fragmentTransaction = fragmentManager.{@link android.app.FragmentManager#beginTransaction()};
Каждая транзакция является набором изменений, выполняемых одновременно. Разработчик может указать все изменения, которые ему нужно выполнить в данной транзакции, вызывая методы {@link android.app.FragmentTransaction#add add()}, {@link android.app.FragmentTransaction#remove remove()} и {@link android.app.FragmentTransaction#replace replace()}. Затем, чтобы применить транзакцию к операции, следует вызвать метод {@link android.app.FragmentTransaction#commit()}.
Впрочем, до вызова метода{@link android.app.FragmentTransaction#commit()} у разработчика может возникнуть необходимость вызвать метод {@link android.app.FragmentTransaction#addToBackStack addToBackStack()}, чтобы добавить транзакцию в стек переходов назад по транзакциям фрагмента. Этим стеком переходов назад управляет операция, что позволяет пользователю вернуться к предыдущему состоянию фрагмента, нажав кнопку Назад.
Например, следующий код демонстрирует, как можно заменить один фрагмент другим, сохранив при этом предыдущее состояние в стеке переходов назад:
// Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit();
В этом коде объект {@code newFragment} замещает фрагмент (если таковой имеется), находящийся в контейнере макета, на который указывает идентификатор {@code R.id.fragment_container}. В результате вызова метода {@link android.app.FragmentTransaction#addToBackStack addToBackStack()} транзакция замены сохраняется в стеке переходов назад, чтобы пользователь мог обратить транзакцию и вернуть предыдущий фрагмент, нажав кнопку Назад.
Если в транзакцию добавить несколько изменений (например, еще раз вызвать {@link android.app.FragmentTransaction#add add()} или {@link android.app.FragmentTransaction#remove remove()}), а затем вызвать {@link android.app.FragmentTransaction#addToBackStack addToBackStack()}, все изменения, примененные до вызова метода {@link android.app.FragmentTransaction#commit commit()}, будут добавлены в стек переходов назад как одна транзакция, и кнопкаНазад обратит их все вместе.
Порядок добавления изменений к объекту {@link android.app.FragmentTransaction} не играет роли за следующими исключениями:
Если при выполнении транзакции, удаляющей фрагмент, не вызвать метод {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()}, при фиксации транзакции фрагмент уничтожается, и пользователь теряет возможность вернуться к нему. В то же время, если вызвать {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} при удалении фрагмента, фрагмент перейдет в состояние остановки и будет возобновлен, если пользователь вернется к нему.
Совет. К каждой транзакции с фрагментом можно применить анимацию перехода, вызвав {@link android.app.FragmentTransaction#setTransition setTransition()} до фиксации.
Вызов метода {@link android.app.FragmentTransaction#commit()} не приводит к немедленному выполнению транзакции. Метод запланирует ее выполнение в потоке пользовательского интерфейса операции (в «главном» потоке), как только у потока появится возможность для этого. Впрочем, при необходимости можно вызвать {@link android.app.FragmentManager#executePendingTransactions()} из потока пользовательского интерфейса, чтобы транзакции, запланированные методом {@link android.app.FragmentTransaction#commit()} были выполнены немедленно. Как правило, в этом нет необходимости, за исключением случаев, когда транзакция является зависимостью для заданий в других потоках.
Внимание! Фиксировать транзакцию методом {@link android.app.FragmentTransaction#commit commit()} можно только до того, как операциясохранит свое состояние (после того, как пользователь покинет ее). Попытка зафиксировать транзакцию после этого момента вызовет исключение. Дело в том, что состояние после фиксации может быть потеряно, если понадобится восстановить операцию. В ситуациях, в которых потеря фиксации не критична, следует вызывать {@link android.app.FragmentTransaction#commitAllowingStateLoss()}.
Хотя {@link android.app.Fragment} реализован как объект, независимый от класса {@link android.app.Activity}, и может быть использован внутри нескольких операций, конкретный экземпляр фрагмента напрямую связан с содержащей его операцией.
В частности, фрагмент может обратиться к экземпляру {@link android.app.Activity} с помощью метода {@link android.app.Fragment#getActivity()} и без труда выполнить такие задачи, как поиск представления в макете операции:
View listView = {@link android.app.Fragment#getActivity()}.{@link android.app.Activity#findViewById findViewById}(R.id.list);
Аналогичным образом операция может вызывать методы фрагмента, получив ссылку на объект {@link android.app.Fragment} от {@link android.app.FragmentManager} с помощью метода {@link android.app.FragmentManager#findFragmentById findFragmentById()} или {@link android.app.FragmentManager#findFragmentByTag findFragmentByTag()}. Например:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
В некоторых случаях необходимо, чтобы фрагмент использовал события совместно с операцией. Хороший способ реализации этого состоит в том, чтобы определить интерфейс обратного вызова внутри фрагмента и потребовать от контейнерной операции его реализации. Когда операция примет обратный вызов через этот интерфейс, она сможет обмениваться информацией с другими фрагментами в макете по мере необходимости.
Пусть, например, у новостного приложения имеются два фрагмента в одной операции: один для отображения списка статей (фрагмент A), а другой—для отображения статьи (фрагмент B). Тогда фрагмент A должен сообщать операции о том, что выбран пункт списка, чтобы она могла сообщить фрагменту B о необходимости отобразить статью. В этом случае интерфейс {@code OnArticleSelectedListener} объявляется во фрагменте A:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
Тогда операция, содержащая этот фрагмент, реализует интерфейс {@code OnArticleSelectedListener} и переопределит метод {@code onArticleSelected()}, чтобы извещать фрагмент B о событии, исходящем от фрагмента A. Чтобы контейнерная операция наверняка реализовала этот интерфейс, метод обратного вызова {@link android.app.Fragment#onAttach onAttach()} во фрагменте A (который система вызывает при добавлении фрагмента в операцию) создает экземпляр класса {@code OnArticleSelectedListener}, выполнив приведение типа объекта {@link android.app.Activity}, который передается методу {@link android.app.Fragment#onAttach onAttach()}:
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnArticleSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); } } ... }
Если операция не реализовала интерфейс, фрагмент генерирует исключение {@link java.lang.ClassCastException}. В случае успеха элемент {@code mListener} будет содержать ссылку на реализацию интерфейса {@code OnArticleSelectedListener} в операции, чтобы фрагмент A мог использовать события совместно с операцией, вызывая методы, определенные интерфейсом {@code OnArticleSelectedListener}. Например, если фрагмент A является расширением класса {@link android.app.ListFragment}, то всякий раз, когда пользователь нажимает элемент списка, система вызывает {@link android.app.ListFragment#onListItemClick onListItemClick()} во фрагменте. Этот метод, в свою очередь, вызывает метод {@code onArticleSelected()}, чтобы использовать событие совместно с операцией:
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onListItemClick(ListView l, View v, int position, long id) { // Append the clicked item's row ID with the content provider Uri Uri noteUri = ContentUris.{@link android.content.ContentUris#withAppendedId withAppendedId}(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } ... }
Параметр {@code id}, передаваемый методу {@link android.app.ListFragment#onListItemClick onListItemClick()}, — это идентификатор строки с выбранным элементом списка, который операция (или другой фрагмент) использует для получения статьи от объекта {@link android.content.ContentProvider} приложения.
Дополнительные сведения о работе с поставщиком контента приводятся в документе Поставщики контента.
Фрагменты могут добавлять пункты меню в Меню вариантов операции (и, следовательно, в Строку действий), реализовав {@link android.app.Fragment#onCreateOptionsMenu(Menu,MenuInflater) onCreateOptionsMenu()}. Однако, чтобы этот метод мог принимать вызовы, необходимо вызывать {@link android.app.Fragment#setHasOptionsMenu(boolean) setHasOptionsMenu()} во время выполнения метода {@link android.app.Fragment#onCreate(Bundle) onCreate()}, чтобы сообщить, что фрагмент намеревается добавить пункты в Меню вариантов (в противном случае фрагмент не примет вызов метода {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu()}).
Любые пункты, добавляемые фрагментом в Меню вариантов, присоединяются к уже существующим. Кроме того, фрагмент принимает обратные вызовы метода {@link android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, когда пользователь выбирает пункт меню.
Разработчик может также зарегистрировать представление в макете своего фрагмента, чтобы предоставить контекстное меню. Для этого следует вызвать метод {@link android.app.Fragment#registerForContextMenu(View) registerForContextMenu()}. Когда пользователь открывает контекстное меню, фрагмент принимает вызов метода {@link android.app.Fragment#onCreateContextMenu(ContextMenu,View,ContextMenu.ContextMenuInfo) onCreateContextMenu()}. Когда пользователь выбирает пункт меню, фрагмент принимает вызов метода {@link android.app.Fragment#onContextItemSelected(MenuItem) onContextItemSelected()}.
Примечание. Хотя фрагмент принимает обратный вызов по событию «выбран пункт меню» для каждого добавленного им пункта, операция первой принимает соответствующий обратный вызов, когда пользователь выбирает пункт меню. Если имеющаяся в операции реализация обратного вызова по событию «выбран пункт меню» не обрабатывает выбранный пункт, событие передается методу обратного вызова во фрагменте. Это справедливо для Меню вариантов и контекстных меню.
Подробные сведения относительно меню см. в руководствах для разработчиков Меню и Строка действий
Управление жизненным циклом фрагмента во многом аналогично управлению жизненным циклом операции. Как и операция, фрагмент может существовать в одном из трех состояний:
Здесь снова просматривается аналогия с операцией: разработчик может сохранить состояние фрагмента с помощью {@link android.os.Bundle} на случай, если процесс операции будет уничтожен, а разработчику понадобится восстановить состояние фрагмента при повторном создании операции. Состояние можно сохранить во время выполнения метода обратного вызова {@link android.app.Fragment#onSaveInstanceState onSaveInstanceState()} во фрагменте и восстановить его во время выполнения {@link android.app.Fragment#onCreate onCreate()}, {@link android.app.Fragment#onCreateView onCreateView()} или {@link android.app.Fragment#onActivityCreated onActivityCreated()}. Дополнительные сведения о сохранении состояния приводятся в документе Операции.
Самое значительное различие в ходе жизненного цикла между операцией и фрагментом состоит в принципах их сохранения в соответствующих стеках переходов назад. По умолчанию операция помещается в управляемый системой стек переходов назад для операций, когда она останавливается (чтобы пользователь мог вернуться к ней с помощью кнопки Назад, как описано в статье Задачи и стек переходов назад). В то же время, фрагмент помещается в стек переходов назад, управляемый операцией, только когда разработчик явно запросит сохранение конкретного экземпляра, вызвав метод {@link android.app.FragmentTransaction#addToBackStack(String) addToBackStack()} во время транзакции, удаляющей фрагмент.
В остальном управление жизненным циклом фрагмента очень похоже на управление жизненным циклом операции. Поэтому практические рекомендации по управлению жизненным циклом операций применимы и к фрагментам. При этом разработчику необходимо понимать, как жизненный цикл операции влияет на жизненный цикл фрагмента.
Внимание! Если возникнет необходимость в объекте {@link android.content.Context} внутри объекта класса {@link android.app.Fragment}, можно вызвать метод{@link android.app.Fragment#getActivity()}. Однако разработчик должен быть внимательным и вызывать метод {@link android.app.Fragment#getActivity()} только когда фрагмент прикреплен к операции. Если фрагмент еще не прикреплен или был откреплен в конце его жизненного цикла, метод {@link android.app.Fragment#getActivity()} возвратит null.
Жизненый цикл операции, содержащей фрагмент, непосредственным образом влияет на жизненый цикл фрагмента, так что каждый обратный вызов жизненного цикла операции приводит к аналогичному обратного вызову для каждого фрагмента. Например, когда операция принимает вызов {@link android.app.Activity#onPause}, каждый ее фрагмент принимает {@link android.app.Fragment#onPause}.
Однако у фрагментов есть несколько дополнительных методов обратного вызова жизненого цикла, которые обеспечивают уникальное взаимодействие с операцией для выполнения таких действий, как создание и уничтожение пользовательского интерфейса фрагмента. Вот эти методы:
Зависимость жизненого цикла фрагмента от содержащей его операции иллюстрируется рисунком 3. На этом рисунке можно видеть, что очередное состояние операции определяет, какие методы обратного вызова может принимать фрагмент. Например, когда операция принимает свой метод обратного вызова {@link android.app.Activity#onCreate onCreate()}, фрагмент внутри этой операции принимает всего лишь метод обратного вызова {@link android.app.Fragment#onActivityCreated onActivityCreated()}.
Когда операция переходит в состояние «возобновлена», можно свободно добавлять в нее фрагменты и удалять их. Таким образом, жизненный цикл фрагмента может быть независимо изменен, только пока операция остается в состоянии «возобновлена».
Однако, когда операция выходит из этого состояния, продвижение фрагмента по его жизненному циклу снова осуществляется операцией.
Чтобы суммировать все сказанное в этом документе, рассмотрим пример операции, использующей два фрагмента для создания макета с двумя панелями. Операция, код которой приведен ниже, включает в себя один фрагмент для отображения списка пьес Шекспира, а другой — для отображения краткого содержания пьесы, выбранной из списка. В примере показано, как следует организовывать различные конфигурации фрагментов в зависимости от конфигурации экрана.
Примечание. Полный исходный код этой операции находится в разделе {@code FragmentLayout.java}.
Главная операция применяет макет обычным способом, в методе {@link android.app.Activity#onCreate onCreate()}:
{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java main}Здесь применяется макет {@code fragment_layout.xml}:
{@sample development/samples/ApiDemos/res/layout-land/fragment_layout.xml layout}Пользуясь этим макетом, система создает экземпляр класса {@code TitlesFragment} (список пьес), как только операция загрузит макет. При этом объект {@link android.widget.FrameLayout} (в котором будет находиться фрагмент с кратким содержанием) занимает место в правой части экрана, но поначалу остается пустым. Как будет показано ниже, фрагмент не помещается в {@link android.widget.FrameLayout}, пока пользователь не выберет элемент в списке.
Однако не все экраны достаточно широки, чтобы отображать краткое содержание рядом со списком пьес. Поэтому описанный выше макет используется только при альбомной ориентации экрана и хранится в файле {@code res/layout-land/fragment_layout.xml}.
Когда же устройство находится в книжной ориентации, система применяет макет, приведенный ниже, который хранится в файле{@code res/layout/fragment_layout.xml}:
{@sample development/samples/ApiDemos/res/layout/fragment_layout.xml layout}В этом макете присутствует только объект {@code TitlesFragment}. Это означает, что при книжной ориентации устройства виден только список пьес. Когда пользователь нажимает на элемент списка в этой конфигурации, приложение запускает новую операцию для отображения краткого содержания, а не загружает второй фрагмент.
Далее можно видеть, как это реализовано в классах фрагмента. Вначале идет код класса {@code TitlesFragment}, отображающий список пьес Шекспира. Этот фрагмент является расширением класса {@link android.app.ListFragment} и использует его функции для выполнения основной работы со списком.
Изучая код, обратите внимание на то, что в качестве реакции на нажатие пользователем элемента списка возможны две модели поведения. В зависимости от того, какой из двух макетов активен, либо в рамках одной операции создается и отображается новый фрагмент с кратким содержанием (за счет добавления фрагмента в объект {@link android.widget.FrameLayout}), либо запускается новая операция (отображающая фрагмент).
{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java titles}Второй фрагмент, {@code DetailsFragment}, отображает краткое содержание пьесы, выбранной в списке {@code TitlesFragment}:
{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details}Вспомним код класса {@code TitlesFragment}: если пользователь нажимает на пункт списка, а текущий макет не включает в себя представление {@code R.id.details} (которому принадлежит фрагмент {@code DetailsFragment}), то приложение запускает операцию {@code DetailsActivity} для отображения содержимого элемента.
Далее идет код класса {@code DetailsActivity}, который всего лишь содержит объект {@code DetailsFragment} для отображения краткого содержания выбранной пьесы на экране в книжной ориентации:
{@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java details_activity}Обратите внимание, что в альбомной конфигурации эта операция самостоятельно завершается, чтобы главная операция могла принять управление и отобразить фрагмент {@code DetailsFragment} рядом с фрагментом{@code TitlesFragment}. Это может произойти, если пользователь запустит операцию {@code DetailsActivity} в книжной ориентации экрана, а затем перевернет устройство в альбомную ориентацию (в результате чего текущая операция будет перезапущена).
Дополнительные образцы кода, использующего фрагменты (и файлы с полным исходным кодом этого примера), доступны в приложении-примере API Demos в разделе ApiDemos (которое можно загрузить из компонента Samples SDK).