a

Фрагменти

Fragment представляє поведінку частини користувацького інтерфейсу в Activity. Ви можете комбінувати декілька фрагментів в одній активності, щоб побудувати багато-плановий UI та повторно використовувати фрагмент в багатьох активностях. Ви можете думати про фрагмент як про модульну секцію активності, що має свій власний життєвий цикл, отримує свої власні події вводу, та яку ви можете додати або видалити під час роботи активності (на кшталт "підактивності", що ви можете повторно використати в різних активностях).

Фрагмент зобов’язаний завжди бути вбудованим в активність та життєвий цикл фрагменту напряму пов’язаний з життєвим циклом активності. Наприклад, коли активність стає на паузу, це також відбувається зі всіма її фрагментами, та коли активність руйнується, те ж відбувається і з фрагментами. Однак, докі активність працює (знаходиться в стані відновлено життєвого циклу), ви можете маніпулювати кожним фрагментом незалежно, наприклад, додавати та видаляти їх. Коли ви виконуєте таку фрагментну транзакцію, ви також можете додати її в стек, що управляється активністю: кожне входження в стек активності є записом фрагментної транзакції, що була виконана. Зворотній стек дозволяє користувачеві відмінити фрагментну транзакцію (перейти назад), нажавши кнопку Back.

Коли ви додаєте фрагмент як частину розташування вашої активності, вона живе в ViewGroup в ієрархії переглядів активності, та фрагмент визначає власне розташування переглядів. Ви можете вставити фрагмент в розташування вашої активності, декларуючи фрагмент в файлі розташування активності, як елемент <fragment>, або з вашого коду застосування, додаючи його до існуючого ViewGroup. Однак, фрагмент не потребує бути частиною розташування активності; ви можете також використати фрагмент без його власного UI, як невидимий робітник для активності.

Цей документ описує, як побудувати ваше застосування для використання фрагментів, включаючи як фрагменти можуть обробляти власний стан, коли додаютья до зворотнього стеку активності, розділяти події з активністю та іншими фрагментами в активності, впливати на рядок заголовку активності, та інше.

Філософія дизайну


Android вводить фрагменти в Android 3.0 (API level 11), здебільшого для підтримки більш динамічного та гнучкого дизайну UI на великих екранах, таких як планшети. Оскільки екрани планшетів значно більші, ніж для смартфонів, стає більше місця для комбінації та заміни UI компонент. Фрагменти дозволяють такі дизайни, не змушуючи вас керувати складними змінами в ієрархії переглядів. Поділяючи розташування активності на фрагменти, ви будете в змозі модифікувати вигляд активності під час роботи, та зберігати ті зміни в зворотньому стеку, що обслуговується активністю.

Наприклад, застосування новин може використовувати один фрагмент для відображення списку заголовків, зліва, та інший фрагмент для відображенния статті зправа — обоє фрагмента з’являються в одній активності, пліч о пліч, та кожний фрагмент має  власний набір методів зворотнього виклику та обробляє власні подіії користувацького ввода. Таким чином, замість використання однієї активності для вибору статті та другої для її читання, користувач може обрати статтю та прочитати її в одній активності , як проілюстровано в планшетному розташуванні на мал. 1.

Ви маєте розробляти кожний фрагмент як модульний та повторно використовуваний елемент активності. Таким чином, оскільки кожний фрагмент визначає своє власне розташування та свою власну поведінку, зі своїми власними зворотніми викликами життєвого циклу, ви можете включати один фрагмент в декілька активностей, так що ви маєте розробляти для повторного використання, та уникати прягого маніпулювання одного фрагменту з іншого фрагменту. Це особливо важливо, оскільки модулярні фрагменти дозволяють вам змінювати ваші комбінації фрагментів для різних розмірів екрану. Коли розробляється застосування для підтримки обох, планшетів та смартфонів, ви можете повторно використовувати ваші фрагменти в різних комбінаціях розташувань, для оптимізації користувацького досвіду, на основі доступного простору екрана. Наприклад, на смартфоні може бути необхідним розділити фрагменти, щоб запровадити одно-панельний UI, коли більше одного не може поміститись в тій же активності.

Малюнок 1. Приклад того, як два UI модулі визначені фрагментами, можуть бути скомбіновані в одну активність для планшетного дизайну, але розділені для мобільного дизайну.

Наприклад — щоб продовжити з прикладом читача новин  — застосування может вбудувати два фрагменти в Activity A, коли виконується на пристрої з екраном розміру планшету. Однак на екрані розміру смертфону, немає досить місця для обох фрагментів, так що Activity A включає тільки фрагмент для списку статей, та коли користувач обирає статтю, запусається активність Activity B, що включає другий фрагмент, щоб прочитати статтю. Таким чином, застосування підтримує обоє, планшети та смартфони, повторно використовуючи фрагменти в різних комбінаціях, як іллюструється на мал. 1.

Для додаткової інформації щодо розробки вашого застосування з різними комбінаціями фрагментів для різних конфігурацій екрана, дивіться інструкції щодо  Подтримки планшетів та смартфонів.

Створення фрагменту


Малюнок 2. Життєвий цикл фрагменту (коли активність виконується).

Щоб створити фрагмент ви мусите створити підклас  Fragment (або його існуючого підкласу ). Клас Fragment має код, що виглядає майже як Activity. Він містить методи зворотнього виклику, подібні до активності, такі як onCreate(), onStart(), onPause() та onStop(). Фактично, якщо ви переробляєте існуюче застосування Android для використання фрагментів, ви можете просто перенести код з методів зворотнього виклику вашої активності у відповідні методи зворотнього виклику вашого фрагменту.

Зазвичай, вам слідує реалізувати щонайменше наступні методи життєвого циклу:

onCreate()
Система викликає його, коли створює фрагмент. В вашій реалізації вам слідує ініціалізувати основні компоненти фрагменту, що ви бажаєте зберігати, коли фрагмент стає на паузу або зупиняється,та потім відновлює роботу.
onCreateView()
Система викликає його, коли настає час для фрагменту вперше малювати власний користувацькій інтерфейс. Щоб намалювати UI для вашого фрагменту, ви маєте повернути View з цього методу, що є коренем вашого розташування фрагменту. Ви можете повернути null, якщо фрагмент не надає  UI.
onPause()
Система викликає цей метод, як перший сигнал, що користувач залишає фрагмент  (але це не завжди означає, що фрагмент буде зруйновано). Це, зазвичай, те місце, де ви маєте закріпити всі зміни, що мають бути збережені за поточною користувацькою сессією (оскільки користувач може не повернутись).

Більшість застосувань повинні реалізовати щонайменьше ці три методи для кожного фрагменту, але є декілька інших методів зворотнього виклику, що вам слідує також використовувати для обробки різних стадій життєвого циклу фрагменту. Всі методи зворотнього виклику життєвого циклу дискутуються більш детально в розділі щодо Обробки життєвого циклу фрагменту.

Також є декілька підкласів, що ви можете бажати розширити, замість базового класу  Fragment:

DialogFragment
Відображує плаваючий діалог. Використовуючи цей клас для створення діалогу  є гарною альтернативою для використання допоміжних методів діалогів в класі Activity , оскільки ви можете вставити діалог фрагменту в стек фрагментів, що керується активністю, дозволяючи користувачеві повернутись в відкладений фрагмент.
ListFragment
Відображує список елементів, що керуються адаптером (таким як SimpleCursorAdapter), подібно до ListActivity. Це провадить декілька методів для керування переглядом списку, такі як зворотній виклик onListItemClick() для обробки подій клацання.
PreferenceFragment
Відображує ієрархію об’єктів  Preference ввигляді списку, подібно до  PreferenceActivity. Це корисно при створені активності "налаштування" для вашого застосування.

Додавання користувацького інтерфейсу

Фрагмент звичайно використовується як частина користувацького інтерфейсу активності та вкладає власне розташування в активність.

Щоб запровадити розташування для фрагменту, ви повинні реалізувати метод зворотнього виклику  onCreateView(), що викликаєтся системою Android, коли настає час для фрагменту малювати своє розташування. Ваша реалізація методу має повернути View, що є коренем розташування вашого фрагменту.

Зауваженя: Якщо ваш фрагмент є підкласом  ListFragment, реалізація по замовчанню повертає ListView від onCreateView(), так що ви не повинні реалізовати його.

Щоб повернути розташування від onCreateView(), ви можете розгорнути його з ресурсу розташування, визначеному в XML. Щоб допомогти вам зробити це, onCreateView() провадить об’єкт  LayoutInflater.

Наприклад, ось підклас Fragment, що завантажує розташування з файлу  example_fragment.xml:

public static class ExampleFragment extends Fragment {
   
@Override
   
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             
Bundle savedInstanceState) {
       
// Розгорнути розташування для цього фрагменту
       
return inflater.inflate(R.layout.example_fragment, container, false);
   
}
}

Параметр container, переданий до onCreateView() є батьківським ViewGroup (з розташування активності), в якому може бути вставлений ваш фрагмент. Параметр  savedInstanceState є Bundle, що провадить дані щодо попереднього екземпляру фрагменту, якщо фрагмент відновлюється (відновлення стану дискутується більше в розділі  Обробка життєвого циклу фрагменту).

Метод inflate() приймає три аргументи:

  • Ресурсний ID розташування, що ви бажаєте розгорнути.
  • ViewGroup буде батьком розгорнутого розташування. Передача container є важловою, щоб система застосувала параметри до кореневого перегляду розгорнутого розташування, вказаного батьківсьго перегляду, де він розташовується.
  • Логічне значення вказує, коли розгонутие розташування повинне бути додано до  ViewGroup (другий параметр) протягом розгортання. (В нашому випадку це false,  оскільки система вже вставляє розгорнуте розташування в container — передача true буде створювати зайву групу переглядів в фінальному розташувані ).

Тепер ви побачили, як створити фрагмент, що провадить розташування. Тепер ви маєте додати фрагмент до активності.

Додавання фрагменту до активності

Звичайно, фрагмент додає частину UI до домашньої активності, що вбудовується як частина загальної ієрархії переглядів активності. Є два шляхі, як ви можете додати фрагмент до розташування активності:

  • Декларуйте фрагмент в файлі розташування активності.

    В цьому випадку ви можете задати властивості для фрагменту так, якби це був перегляд. Наприклад, ось файл розташування для активності з двома фрагментами:

    <?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>

    Атрибут android:name у  <fragment> задає клас Fragment, потрібний для створення екземпляру розташування.

    Коли система створює це розташування активності, вона створює екземпляр кожного фрагменту, вказаного в розташуванні, та виклиає метод onCreateView() для кожного з них, щоб отримати розташування кожного фрагменту. Система вставляє View, що повертає фрагмент, прямо замість елементу <fragment>.

    Зауваження: Кожний фрагмент потребує унікального ідентифікатору, щоб система могла використовувати його для відновлення фрагменту, коли активність рестартовано (та який ви можете використовувати для захоплення фрагменту для виконання транзакцій, наприклад для його видалення). Є три шляхи запровадити ID для фрагменту:

    • Надати атрибут android:id з уніклаьним ID.
    • Надати атрибут android:tag з уніклаьною строкою.
    • Якщо ви не провадите жодного з попередніх двох, система використовує ID контейнерного перегляду.
  • Або програмно додати фрагмент до існуючої ViewGroup.

    Кожного разу, коли ваша активність працює, ви можете додати фрагменти до розташування активності. Вам просто потрібно задати ViewGroup, в якій розташувати фрагмент.

    Щоб зробити транзакції в вашій активності (такі, як додавання, видалення або заміна фрагменту), ви маєте викоирстовувати API з FragmentTransaction. Ви можете отримати екземпляр FragmentTransaction з вашої  Activity наступним чином:

    FragmentManager fragmentManager = getFragmentManager()
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

    Після цього ви можете додати фрагмент, використовуючи метод add(), задаючи фрагмент, що треба додати, та перегляд, куд його треба додати. Наприклад:

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction
    .add(R.id.fragment_container, fragment);
    fragmentTransaction
    .commit();

    Перший аргумент, переданий в add(), є ViewGroup, в якій фрагмент має розташовуватись, задане через ID ресурсу, та другий параметр є фрагмент, що треба додати.

    Коли ви зробили ваші зміни за допомогою  FragmentTransaction, ви маєте викликати commit(), щоб зміни набули чинності.

Додавання фрагменту без UI

Приклад вище показує, як додати фрагмент до вашої активності, так, щоб провадити UI. Однак, ви також можете використовувати фрагмент щоб запровадити фонову поведінку для активності без показу додаткового UI.

Щоб додати форагмент без  UI, додатйте фрагмент в коді активності з використанням  add(Fragment, String) (надаючи унікальний рядок, "тег" для фрагменту, замість ID перегляду). Це додає фрагмент, але, оскількі він не асоційований з переглядом в розташуванні активності, він не отримує виклик onCreateView(). Так що вам не треба реалізуват цей метод.

Надавати рядок тегу для фрагменту не є напряму для не-UI фрагментів — ви можете також надати рядки тегів до фрагментів, що мають UI — але якщо фрагмент не  має UI, тоді рядок тегу є єдиним шляхом ідентифікувати його. Якщо піздніше ви бажаєте отримати фрагмент з активності,  вам треба використовувати findFragmentByTag().

Для прикладу активності, що використовує фрагмент в якості фонового робітника без UI, дивіться приклад FragmentRetainInstance.java, що включений в приклади SDK (доступно через Android SDK Manager) та розташовано в вашій системі як <sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

Управління фрагментами


Для управління фрагментами в вашій активності, вам треба використати FragmentManager. Щоб отримати його, викличте getFragmentManager() в вашій активності.

Деякі речі, що ви можете робити з  FragmentManager включають:

  • Отримати фрагменти, що існують для активності за допомогою findFragmentById() (для фрагментів, що провадять UI в розташуванні активності) або findFragmentByTag() (для фрагментів, що не провадять UI).
  • Висувати фрагменти зі стеку за допомогою popBackStack() (симулюючи команду Back від користувача).
  • Реєструвати слухача для відсліжування змін в стеку, за допомогою addOnBackStackChangedListener().

Для додаткової інформації щодо ціх та інших методів, посилайтесь на документацію класу FragmentManager.

Як продемонстровано в попередньому розділі, ви можете  також використовувати  FragmentManager для відкриття FragmentTransaction, що дозволяє вам виконувати транзакції, такі, як додавання та видалення фрагментів.

Виконання транзакцій з фрагментами


Чудова можливість щодо використання фрагментів в вашій активності це можливість додавати, видаляти, замінювати, та виконувати інші дії з ними, у відповідь на взаємодію користувача. Кожний набір змін, що ви закріплюєте в активності, називається транзакцією, і ви можете виконати його з використанням APIs в FragmentTransaction. Ви також можете зберегти кожну транзакцію в стеку, що керується активністю, дозволяючи користувачу навігацію в зворотньому напрямку по змінах фрагментів (подібно до навігації назад по активністям).

Ви можете отримати екземпляр FragmentTransaction з FragmentManager таким чином:

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

Кожна транзакція є набором змін, що ви бажаєте виконати одночасно. Ви можете встановити всі зміни, що ви бажаєте виконати для даної транзакції за допомогою методів. таких як add(), remove() та replace(). Після цього, щоб застосувати транзакцію до активності, ви маєте викликати commit().

Однак, перед тим, як ви викличите commit(), ви можете забажати addToBackStack(), для того, щоб додати транзакцію до стеку фрагментних транзакцій. Цей зворотній стек керується активністю, та дозволяє користувачу повернутись до попереднього стану фрагментів, натискаючи кнопку Back.

Наприклад, ось як ви можете замінити один фрагмент на інший, та зберегти попередній стан в стеку:

// Створюємо новий фрагмент та транзакцію
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();

// Замінюємо будь-що в перегляді fragment_container на цей фрагмент,
// та додаємо транзакцію до стеку
transaction
.replace(R.id.fragment_container, newFragment);
transaction
.addToBackStack(null);

// Закріплюємо транзакцію
transaction
.commit();

В цьому прикладі  newFragment замінює будь-який фрагмент (якщо такий був), що до цього був в контейнері розташування з ідентифікатором R.id.fragment_container. Викликаючи addToBackStack(), транзакція заміни зберігається в стеку, так що користувач може повернути транзакцію, та перенести назад фрагмент, натискаючи кнопку Back.

Якщо ви додасте декілька змін до транзакції (таких, як інші add() або remove()), та викличите addToBackStack(), тоді всі зміни, що ви зробите до виклику commit(), будуть додані до стеку як єдина транзакція, та кнопка Back буде повертати ії всі розам.

Порядок, в якому ви додаєте зміни до FragmentTransaction не має значення, за такими винятками:

  • Ви маєте викликати commit() останнім
  • Якщо ви додаєте декілька фрагментів в той же контейнер, тоді порядок, в якому ви додаєте їх визначає порядок, в якому вони з’являтимуться в ієрархії перелядів

Якщо ви не викликаєте addToBackStack() коли ви виконуєте транзакцію, що видаляє фрагмент, тоді цей фрагмент руйнується коли транзакція фіксується, та користувач не може повернутись до неї. Навпаки, якщо ви викликаєте addToBackStack() коли видаляєте фрагмент, тоді фрагмент зупиняється, та буде відновлений, якщо користувач певернетья назад.

Підказка: Для кожної транзакції фрагменту ви можете застосувати анімацію транзакції, викликаючи setTransition() перед закріпленням.

Виклик commit() не виконує транзакцію безпосередньо. Скоріше, він планує її для виконання на UI потоці активності (потік "main"), так швидко, як потік зможе виконати її. Коли необхідно, однак. ви можете викликати executePendingTransactions() з вашого UI потоку, щоб безпосередньо виконату транзакцію, що була надіслана commit(). Робити це зазвичай не треба, за винятком коли транзакція є залежністю для завдань в інших потоках.

Попередження: Ви можете закріпити транзакцію, використовуючи commit() тільки перед до того, як активність зберігає стан (коли користувач залишає активність). Якщо ви намагаєтесь закріпитись після цієї точки, буде викликане виключення. Це тому. що стан після закріплення може бути втрачене, якщо активність потім буде потребувати відновлення. Для ситуацій, в яких буде прийнятним втратити закріплення, використовуйте commitAllowingStateLoss().

Комунікація з Activity


Хоча Fragment реалізований як об’єкт, що незалежний від Activity, та може бути використаний в багатьох активностях, даний екземпляр фрагменту напряму прив’язаний до активності, що містить його.

Більш конкретно, фрагмент може отримати доступ до екземпляру Activity через getActivity() та просто виконувати завдання, такі, як знайти перегляд в розташуванні активності:

View listView = getActivity().findViewById(R.id.list);

Подябно до цього, ваша активність може викликати методи вашого фрагменту, отримавши посилання на Fragment з FragmentManager, використовуючи findFragmentById() або findFragmentByTag(). Наприклад:

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

Створення функцій зворотнього виклику подій для активності

В деяких випадках, вам може знадобитись, щоб фрагмент поділяв події з активністю. Гарний шлях зробити це - визначити інтерфейс зворотнього виклику в фрагменті, та змусити, щоб активність реалізувала його. Коли активність отримує зворотній виклик через інтерфейс, вони може поділитись інформацією з іншими фрагментами в розташуванні, коли це необхідно.

Наприклад, якщо застосування новин має дві активності — одне для відображення списку статей (фрагмент A) та інше для відображення статті (фрагмент B) — тоді фрагмент A має сповістити активність, коли обирається один елемент зі списку, так щоб потім сповістити фрагмент B для відображання цієї статті. В цьому випадку екземпляр OnArticleSelectedListener декларується в фрагменті A:

public static class FragmentA extends ListFragment {
   
...
   
// Контейнер Activity має реалізувати цей інтерфейс
   
public interface OnArticleSelectedListener {
       
public void onArticleSelected(Uri articleUri);
   
}
   
...
}

Потім активність, що приймає фрагмент, реалізує інтерфейс OnArticleSelectedListener та переписує onArticleSelected() для того, щоб сповістити фрагмент B по події з фрагменту A. Щоб впевнитись, що активність реалізує цей інтерфейс, метод зворотнього виклику A onAttach() (що є системним викликом при додаванні фрагменту до активності) створює екземпляр OnArticleSelectedListener приводячи передану Activity в 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() + " має реалізувати OnArticleSelectedListener");
       
}
   
}
   
...
}

Якщо активність не реалізує цей інтерфейс, тоді фрагмент викликає ClassCastException. В разі умпіху член mListener містить посилання на реалізацію OnArticleSelectedListener в активності, так що фрагмент A може поділяти події з активністю, викликаючи методи, визначені в інтерфейсі OnArticleSelectedListener. Наприклад, якщо фрагмент A є розширенням ListFragment, кожний раз, коли користувач клацає на екземплярі списку, система викликає onListItemClick() в фрагменті, що в свою чергу викликає onArticleSelected() для розділення події з активністю:

public static class FragmentA extends ListFragment {
   
OnArticleSelectedListener mListener;
   
...
   
@Override
   
public void onListItemClick(ListView l, View v, int position, long id) {
       
// Додати клацнутий елемент від ID рядка з Uri контент провайдера
       
Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
       
// Надіслати подію та Uri в активність
        mListener
.onArticleSelected(noteUri);
   
}
   
...
}

Параметр id, переданий до onListItemClick() є ID рядка клацтнутого елементу, яку активність (або інший фрагмент) використовує для підтягування статті ContentProvider застосування.

Більше інформації щодо використання контент провайдерів доступно в документі Контент провайдери.

Додавання елементів до Action Bar

Ваші фрагменти можуть додавати елеленти меню до Меню Опцій застосування (та, дідповідно, до Заголовку) реалізуючи onCreateOptionsMenu(). Щоб цей метод отримував виклики, одначе, ви маєте викликати setHasOptionsMenu() на протязі onCreate(), щоб вказати, що фрагмент бажає додавати елементи до Меню Опцій (інакше, фрагмент не буде отримувати виклик onCreateOptionsMenu()).

Любі елементи, що ви потім додасте до Меню Опцій від фрагмента, будуть додані до існуючих елементів меню. Фрагмент також отримує зворотні виклики на onOptionsItemSelected(), коли обираються елементи меню.

Ви також можете реєструвати перегляд в вашому розташуванні фрагменту, щоб запровадити контекстне меню, викликаючи registerForContextMenu(). Коли користувач відкриває контекстне меню, фрагмент отримує виклик onCreateContextMenu(). Коли користувач обирає елемент, фрагмент отримує виклик на onContextItemSelected().

Зауваження: Хоча ваш фрагмент отримує зворотній виклик по обраному елементу, для кожного елелменту меню, що він додає, активність перша отримує відповідний виклик, коли користувач обирає елемент меню. Якщо реалізація активності зворотнього виклику не обробляє обраний елелмент, тоді подія передається до обробника фрагменту. Це вірно для для Меню Опцій та для контекстного меню.

Для додаткової інформації щодо меню звртайтесь до документів розробника Меню та Заголовок.

Обробка життєвого циклу фаргменту


Figure 3. The effect of the activity lifecycle on the fragment lifecycle.

Управління життєвим циклом фрагменту досить схоже на управління життєвим циклом активності. Подібно до активності, фрагмент може існувати в трьох станах:

Відновлено
Фрагмент є видімим в робочій активності.
Призупинений (на паузі)
Інша активність на передньому плані та в фокусі, але активність, в якій цей фрагмент живе, все ще видімий (активність першого плана частково прозора або не закриває весь екран).
Зупинено
Фрагмент невидімий. Або приймаюча активність була зупинена, або фрагмент був видалений з активності, але доданий до стеку. Зупинений фрагмент все ще живий (всі стани та інформація членів зберігається системой). Однак він більше не відображується користувачеві, та буде вбитий, коли буде вбира активність.

Також, як для активності, ви можете зберігти стан фрагменту, використовуючи Bundle, в разі, коли процесс активності вбито, та вам треба відновити стан фрагменту, коли відтворюється активність.

Ви можете зберігти стан на протязі зворотнього виклику onSaveInstanceState() фрагменту, та відновити його на протязі або onCreate(), onCreateView(), або onActivityCreated(). Для додаткової інформації щодо зберігання стану, дивіться документ Activities.

Найбільш значуща відмінність в життєвому циклі між активністю та фрагментом, це як вони зберігаються в відповідному стеку. Активність розміщується в стеку активностей, що керується системою, коли вона зупиняється, по замовчанню  (так що коли користувач повертається за допомогою кнопки Back, як дискутується в Завдання та Back Стек). Однак, фрагмент покладається в стек, що керується активністю, тільки коли ви явно наказуєте, щоб цей екземпляр був збережений, викликаючи addToBackStack() на протязі транзакції, що виділяє цей фрагмент.

В іншому управління життєвим циклом фрагменту є дуже подібним до управління життєвим циклом активності. Так що ті ж методи для управління життєвим циклом активності також застосовуються і для фрагментів. Що ви також маєте розуміти, це те, як життя активності впливає на життя фрагменту.

Попередження: Якщо вам треба об’єкт Context в вашому Fragment, ви можете викликати getActivity(). Однак будьте уважні, викликаючи getActivity() тільки коли фрагмент приєднаний до активності. Коли фрагмент ще не приєднаний, або був від’єднаний в кінці свого життєвого циклу, getActivity() буде повертати null.

Координація з життєвим циклом активності

Життєвий цикл активності, в якому живе фрагмент, напряму впливає на фрагмент, так що кожний зворотній виклик життєвого циклу для активності призводить до подібного виклику для фрагменту. Наприклад, коли активність отримує onPause(), кожний фрагмент в активності отримує onPause().

Однак фрагменти мають декілька додаткових зворотніх викликів, що обробляють унікальну взаємодію з активністю, щоб виконувати дії щодо побудови та знищення UI фрагменту. Ці додаткові методи зворотнього виклику такі:

onAttach()
Викликається, коли фрагмент був асоційований з активністю (Activity передається тут).
onCreateView()
Викликається для створення ієрархії переглядів, асоційованих з фрагментом.
onActivityCreated()
Викликається, коли повертається метод активності onCreate().
onDestroyView()
Викликається, коли ієрархія переглядів, асоційована з фрагментом, була видалена.
onDetach()
Викликається, коли фрагмент був деасоційований з активністю.

Перебіг життєвого циклу фрагменту, як воно залежить від приймаючої активності, проілюстровано на мал.  3. На цьому малюнку ви можете побачити, як кожний наступний стан активності визначає, які методи зворотнього виклику буде отримувати фрагмент. Наприклад, кои активність отримала виклик onCreate(), фрагмент в активності отримує не більше, ніж зворотній виклик onActivityCreated().

Коли активність досягає відновленого стану, ви можете вільно додавати та видаляти фрагменти з активності. Таким чином, тільки коли активність є в відновленому стані, життєвий цикл фрагменту може змінюватись незалежно.

Однак, коли активність залишає відновлений стан, фрагмент знову проштовхується через життєвий цикл активності.

Приклад


Щоб зібрати все що дискутувалось в цьому документі до купи, ось приклад активності, що використовує два фрагменти, щоб створити дво-плановий розклад. Активність нижче включає один фрагмент для відображення списку вистав Шекспіра, та другий, щоб відображати стислий зміст вистави, що була обрана. Це також демонструє, як провадити різні конфігурації для фрагментів, на основі конфігурації екрана. 

Зауваження: Повний первинний код для цієї активності доступний в FragmentLayout.java.

Головна активність застосовує розташування звичайним шляхом, під час onCreate():

@Override
protected void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);

    setContentView
(R.layout.fragment_layout);
}

Застосоване розташування в файлі  fragment_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:orientation="horizontal"
   
android:layout_width="match_parent" android:layout_height="match_parent">

   
<fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
           
android:id="@+id/titles" android:layout_weight="1"
           
android:layout_width="0px" android:layout_height="match_parent" />

   
<FrameLayout android:id="@+id/details" android:layout_weight="1"
           
android:layout_width="0px" android:layout_height="match_parent"
           
android:background="?android:attr/detailsElementBackground" />

</LinearLayout>

Використовуючи це розташування, система робить екземпляр TitlesFragment (що перелічує назіи п’єс) по мірі того, як активність завантажить розташування, тоді як FrameLayout (де буде фрагмент, що показує опис подій вистави) споживає простір з правої сторони екрану, але спочатку є пустим. Як ви побачите нижче, його немає, доки користувач не обере елемент в списку, в тоді цей фрагмент розташовується в FrameLayout.

Однак, не всі конфігурації екрана досить широкі, щоб показувати обоє, список та пдісумок, поряд. Так що розташування вище використовується тільки для ландшафтної конфігурації, зберігаючи його як res/layout-land/fragment_layout.xml.

Таким чином, коли екран знаходиться в портретній орієнтації, система застосовує наступние розташування, яке збережене в res/layout/fragment_layout.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   
android:layout_width="match_parent" android:layout_height="match_parent">
   
<fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
           
android:id="@+id/titles"
           
android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

Це розташування включає тільки TitlesFragment. Це означає, що коли пристрій знаходиться в портретній орієнтації, відображається тільки список назв. Так що коли користувач клацає на елементі списку в цій конфігурації, застосування буде починати нову активність для відображення підсумку, замість завантаження другого фрагменту.

Далі, ви можете побачити, як це досягається в класі фрагменту. Перший TitlesFragment, що відображує список п’єс Шекспіра. Цей фрагмент розширює ListFragment, та покладаєтсья на його для обробки більшості роботи перегляду списку.

Коли ви подивитесь на цей код, зауважте, що є дві можливі поведінки, коли користувач клацає по елементу списку: в залежності від того, яке з двох розташувань активне, може або створити та відобразити новий фрагмент для відображення деталей в тій же активності (додаючи фрагмент до FrameLayout), або почати нову активність (де може бути відображений цей фрагмент).

public static class TitlesFragment extends ListFragment {
   
boolean mDualPane;
   
int mCurCheckPosition = 0;

   
@Override
   
public void onActivityCreated(Bundle savedInstanceState) {
       
super.onActivityCreated(savedInstanceState);

       
// Заповнюємо список за допомогою статичного масиву.
        setListAdapter
(new ArrayAdapter<String>(getActivity(),
                android
.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

       
// Перевірити, чи ми маємо фрейм, в якому треба розташувати
       
// фрагмент прямо в оточующому UI.
       
View detailsFrame = getActivity().findViewById(R.id.details);
        mDualPane
= detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;

       
if (savedInstanceState != null) {
           
// Відновити останній стан для клацнутої позиції.
            mCurCheckPosition
= savedInstanceState.getInt("curChoice", 0);
       
}

       
if (mDualPane) {
           
// В дво-плановому режимі перегляд списку підсвічує обраний елемент.
            getListView
().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
           
// Впевніться що наш UI в коректному стані.
            showDetails
(mCurCheckPosition);
       
}
   
}

   
@Override
   
public void onSaveInstanceState(Bundle outState) {
       
super.onSaveInstanceState(outState);
        outState
.putInt("curChoice", mCurCheckPosition);
   
}

   
@Override
   
public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails
(position);
   
}

   
/**
     * Допоміжна функція для відображення деталей обраного елементу, або через
     * відображення фрагменту на місці в поточному UI, або розпочинаючи повністю
     * нову активність, в якій відображається фрагмент.
     */

   
void showDetails(int index) {
        mCurCheckPosition
= index;

       
if (mDualPane) {
           
// Ми можемо відобразити будь-що за допомогою фрагментів, так що
           
// оновлюємо обраний елемент та відображуємо дані.
            getListView
().setItemChecked(index, true);

           
// Перевірити, що фрагмент наразі відображується, замінюємо якщо треба.
           
DetailsFragment details = (DetailsFragment)
                    getFragmentManager
().findFragmentById(R.id.details);
           
if (details == null || details.getShownIndex() != index) {
               
// Зробити новиий фрагмент для відображення цієї секції.
                details
= DetailsFragment.newInstance(index);

               
// Виконати транзакцію, замінюючи у фреймі любий існуючий фрагмент.
                FragmentTransaction ft = getFragmentManager().beginTransaction();
               
if (index == 0) {
                    ft
.replace(R.id.details, details);
               
} else {
                    ft
.replace(R.id.a_item, details);
               
}
                ft
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft
.commit();
           
}

       
} else {
           
// Інакше нам треба запустити нову активність для відображування
           
// фрагменту діалогу з обраним текстом.
           
Intent intent = new Intent();
            intent
.setClass(getActivity(), DetailsActivity.class);
            intent
.putExtra("index", index);
            startActivity
(intent);
       
}
   
}
}

Другий фрагмент, DetailsFragment, відображує підсумки для елементу, обраного зі списку в  TitlesFragment:

public static class DetailsFragment extends Fragment {
   
/**
     * Створити новий екземпляр DetailsFragment, ініціалізований для
     *
відображення текста як 'index'.
     */

   
public static DetailsFragment newInstance(int index) {
       
DetailsFragment f = new DetailsFragment();

       
// Задає індекс як аргумент.
       
Bundle args = new Bundle();
        args
.putInt("index", index);
        f
.setArguments(args);

       
return f;
   
}

   
public int getShownIndex() {
       
return getArguments().getInt("index", 0);
   
}

   
@Override
   
public View onCreateView(LayoutInflater inflater, ViewGroup container,
           
Bundle savedInstanceState) {
       
if (container == null) {
           
// Ми маємо різні розташування, та в одному з них фрейм,
           
// що містить фрагмент, не існує. Фрагмент може також бути створений
// зі збереженого стану
, але немає причини намагатись створювати його
           
// ієрархію перегялів, бо він не буде відображений. Зауважте, це не треба.
           
// Ми можемо просто виконати код нижче, де ми сторимо та повернемо
           
// ієрархію переглядів; це просто ніколи не буде використовуватись.
           
return null;
       
}

       
ScrollView scroller = new ScrollView(getActivity());
       
TextView text = new TextView(getActivity());
       
int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
               
4, getActivity().getResources().getDisplayMetrics());
        text
.setPadding(padding, padding, padding, padding);
        scroller
.addView(text);
        text
.setText(Shakespeare.DIALOGUE[getShownIndex()]);
       
return scroller;
   
}
}

Пригадайте з класу TitlesFragment, що, коли користувач клацає на списку, та поточние розташування не містить перегляд R.id.details (де розташовується DetailsFragment), тоді застосування стартує активність DetailsActivity для відображення вмісту елементу.

Ось DetailsActivity, що просто вбудовує DetailsFragment для відображення делатей обраного елементу коли екран знаходится в портретній орієнтації:

public static class DetailsActivity extends Activity {

   
@Override
   
protected void onCreate(Bundle savedInstanceState) {
       
super.onCreate(savedInstanceState);

       
if (getResources().getConfiguration().orientation
               
== Configuration.ORIENTATION_LANDSCAPE) {
           
// Якщо екран тепер в ландшафтному режимі ми можемо відобразити
           
// діалог поряд зі списком, так що нам не потрібна ця активність.
            finish
();
           
return;
       
}

       
if (savedInstanceState == null) {
           
// На протязі ініціалізації створити фрагмент детального перегляду.
           
DetailsFragment details = new DetailsFragment();
            details
.setArguments(getIntent().getExtras());
            getFragmentManager
().beginTransaction().add(android.R.id.content, details).commit();
       
}
   
}
}

Зауважте, що активність завершує сама себе, якщо конфігурація є ландшафтна, так що головна активність може піти далі, та відображати DetailsFragment поярд з TitlesFragment. Це може трапитись, якщо користувач починає DetailsActivity перебуваючи в портретній орієнтації, але тоді він повратає до ландшафтної (що рестартує поточну активність).

Для додаткових прикладів з використання фрагментів (та повних первинних файлів до них), дивіться приклад API Demos, доступне в ApiDemos (є частиною завантаження Samples SDK component).