Краса з фрагментів. Як поліпшити UI в Android c допомогою класу Fragment. Життєвий цикл фрагмента Продовження доступно тільки учасникам

Розповідаючи про розробку під Android неможливо не згадати фрагменти.

Що таке Fragment?

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

Фрагмент містить в собі елементи інтерфейсу, точно так же, як і Activity, однак, між цими двома поняттями є кілька ключових відмінностей:

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

Реалізація програми без фрагментів

Давайте подивимося на конкретний приклад, щоб краще зрозуміти, як це працює.

Уявімо, що у нас є додаток, що містить два екрани:

  • Екран зі стрічкою новин.
  • Екран з деталями про новини, відкривається при натисканні на елемент списку в попередній Activity.

Ось так воно виглядає на телефоні:


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

А тепер уявіть, що нам також потрібно зробити додаток для планшета:


В цьому випадку ми відображаємо контент з обох екранів на одному, при цьому використовуються одні й ті ж елементи. Виходить, нам потрібна третя Activity, наприклад, TabletFeedActivity.

Чи буде такий підхід працювати? Так. Коректний він? Абсолютно ні.

При використанні такого підходу ми мало того, що створюємо додаткові непотрібні Activity, так ще й не мають змоги перевикористати код - майже весь код в TabletFeedActivity буде просто скопіпащен з FeedActivity і DetailActivity!

І тут нам на допомогу приходять фрагменти!

Реалізація програми з фрагментами

Ми залишаємо FeedActivity і DetailActivity, але вводимо додатково два класи - FeedFragment ( Fragment # 1 на зображенні нижче) і DetailFragment ( Fragment # 2 на зображенні нижче).

У разі використання телефону FeedFragment розташовується в FeedActivity, а DetailFragment - в DetailActivity:

Якщо ж додаток запущено на планшеті, ми додаємо обидва фрагмента в FeedActivity, DetailActivity при цьому не використовується взагалі. Усе!


Таким чином ми не пишемо зайвого коду і наше додаток стає повністю адаптивним.

Також як і у Activity, у фрагмента є життєвий цикл.

У Fragment може бути три стани:

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

Подивіться на таблицю коллбеков фрагмента, які викликаються відповідно зі зміною стану Activity:

Давайте розглянемо деякі з них.

  • onAttach () - Fragment "Прикріплюється" до Activity.
  • onCreate () - Fragment створюється.
  • onCreateView () - викликається для створення елементів інтерфейсу (наприклад, інфлейта з XML).
  • onActivityCreated () - викликається після відпрацювання методу onCreate () в Activity.
  • onDestroyView () - викликається, коли View, створений в onCreateView "відкріплюється" від фрагмента.
  • onDetach () - Fragment "Відкріплюється" від Activity.

В цілому все дуже схоже на Activity, за винятком деяких нових коллбеков.

Давайте ж спробуємо це на практиці

додаємо фрагменти

Давайте створимо додаток, в якому буде два фрагмента:

  • Фрагмент з перемикачами, якими ми вибираємо колір.
  • Фрагмент, просто залитий вибраним кольором.

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

На планшеті буде лише один екран з двохпанельний інтерфейсом, прямо як в наведеному мною вище прикладі.

Спочатку розберемося, як ми можемо створити фрагмент і додати його в Activity.

Створіть новий проект, а в ньому - новий фрагмент:

Public class SelectionFragment extends Fragment (public SelectionFragment () () @Nullable @Override public View onCreateView (LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) (return super.onCreateView (inflater, container, savedInstanceState);))

Зверніть увагу: у фрагмента обов'язково повинен бути конструктор без параметрів, і не повинно бути інших конструкторів, так як при пересоздании фрагмента вони не будуть викликані.

Також створимо файл з версткою для фрагмента - fragment_selection.xml:

Тепер заінфлейтім лейаута всередині методу onCreateView ():

@Nullable @Override public View onCreateView (LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) (View view \u003d inflater.inflate (R.layout.fragment_selection, container, false); view.setBackgroundColor (Color.RED); return view; )

Я задав червоний колір, щоб було зрозуміло, де знаходиться фрагмент.

Існує два способи додавання фрагмента в Activity:

  • Через XML. У цьому випадку не можна буде видалити фрагмент в Рантайм.
  • У Рантайм.

Додавання фрагмента в XML

Спробуємо додати фрагмент в activity_main.xml:

Відкрийте програму:

Додавання фрагмента в Рантайм

В першу чергу замінимо розмітку лейаута MainActivity на таку:

FrameLayout в даному випадку буде служити контейнером для фрагмента.

Тепер в кінці методу onCreate () в MainActivity додамо такий код:

SelectionFragment selectionFragment \u003d new SelectionFragment (); FragmentManager fragmentManager \u003d getFragmentManager (); fragmentManager.beginTransaction () .add (R.id.container, selectionFragment) .commit ();

Ми створюємо фрагмент, отримуємо менеджер фрагментів і додаємо фрагмент в контейнер.

FragmentManager - спеціальний клас, через який відбувається взаємодія з фрагментами.

Зверніть увагу: В Android SDK є дві реалізації фрагментів: звичайна і із Support бібліотеки v4. Незважаючи на те, що в більшості прикладів в інтернеті використовується реалізація з Support Library, насправді в наші дні використовувати її необов'язково, так як вона була створена для роботи фрагментів на більш ранніх версіях Android, ніж 3.0.

Починаючи з 3.0 можна використовувати звичайну реалізацію.

Запустивши додаток ви побачите, що результат залишився тим же:

Створюємо двохпанельний лейаута

Отже, нам потрібно буде два лейаута для MainActivity: один для телефонів, Другий для планшетів.

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


Як бачите, я вибрав кваліфікатор Smallest screen width і ввів значення 600. Таким чином, цей лейаута буде використовуватися тільки на тих пристроях, ширина екрану яких становить хоча б 600dp. Це приблизно відповідає планшету з діагоналлю екрана 7 дюймів.

Стандартна верстка (для телефонів) Буде такою:

Оскільки в Android вітається максимальне використання декларативного підходу при створенні інтерфейсів, а, крім того, нам не потрібно буде видаляти або замінювати SelectionFragment, ми оголосимо його прямо в XML.

Верстка ж для планшетів буде такою:

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

Так як ми тепер не додаємо SelectionFragment динамічно, видаліть весь пов'язаний з ним код з onCreate () в MainActivity.

Також створіть новий емулятор для планшета, наприклад, такий:


Запустіть на ньому додаток:


Як бачите, на планшеті SelectionFragment займає ліві 30% екрану. Решта місце відведено для другого фрагмента, якого поки що немає.

Давайте продовжимо - додамо перемикачі для вибору кольору.

RadioButton і RadioGroup

RadioButton - компонент для створення перемикачів. Оскільки RadioButton не повинен використовуватися в поодинці, існує також і лейаута для нього - RadioGroup.

RadioGroup успадкований від LinearLayout і містить в собі кілька RadioButton. Він керує ексклюзивністю вибору (адже в одиницю часу може бути обраний тільки один перемикач). Завдяки спадкоємства від LinearLayout, він може бути як вертикальним, так і горизонтальним.


Продовження є на платних тарифах

А разом з ним - перевірка домашніх завдань нашими менторами.

Це зовсім недорого - всього від 0 ₽ у місяць!

Останнє оновлення: 30.10.2015

Кожен клас фрагмента успадковується від базового класу Fragment і має свій життєвий цикл, що складається з ряду етапів:

    onAttach (): при виконанні цього методу фрагмент асоціюється з певною activity. На цьому етапі фрагмент і activity ще не повністю ініціалізовані.

    onCreate (): відбувається створення фрагмента. Цей метод викликається після виклику відповідного методу onCreate () у activity.

    onCreateView (): фрагмент створює візуальний інтерфейс

    onActivityCreated (): викликається після створення activity. З цього моменту до компонентів інтерфейсу можна звертатися через метод findViewById ()

    onStart (): викликається, коли фрагмент стає видимим

    onResume (): фрагмент стає активним

    onPause (): фрагмент продовжує залишатися видимим, але вже не активний

    onStop (): фрагмент більше не є видимим

    onDestroyView (): знищується інтерфейс, який представляє фрагмент

    onDestroy (): остаточно знищення фрагмента

У коді класу фрагмента ми можемо перевизначити всі або частину з цих методів.

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

    ListFragment: управляє списком елементів за допомогою одного з адаптерів

    DialogFragment: використовується для створення діалогових вікон

    PreferenceFragment: використовується для управління настройками додатку

Для створення нового фрагмента можна використовувати звичайні класи. Однак серед Android Studio вже пропонує ряд вбудованих шаблонів:

При створенні буде запропоновано встановити ім'я проекту, назва файлу розмітки інтерфейсу і ще ряд опцій:

Генерований Android Studio клас фрагмента буде багато в чому аналогічний раніше використовувалися:

Package com.example.eugene.testapp; import android.app.Activity; import android.net.Uri; import android.os.Bundle; import android.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class BlankFragment extends Fragment (private static final String ARG_PARAM1 \u003d "param1"; private static final String ARG_PARAM2 \u003d "param2"; private String mParam1; private String mParam2; private OnFragmentInteractionListener mListener; // Фабрика для створення фрагмента public static BlankFragment newInstance (String param1, String param2) (BlankFragment fragment \u003d new BlankFragment (); Bundle args \u003d new Bundle (); args.putString (ARG_PARAM1, param1); args.putString (ARG_PARAM2, param2); fragment.setArguments (args); return fragment; ) public BlankFragment () (// Конструктор) @Override public void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); if (getArguments ()! \u003d null) (mParam1 \u003d getArguments (). getString (ARG_PARAM1); mParam2 \u003d getArguments (). getString (ARG_PARAM2);)) @Override public View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) (return inflater.inflate (R.layout.fragment_blank, container, false); ) Public void onButtonPressed (Uri uri) (if (mListener! \u003d Null) (mListener.onFragmentInteraction (uri);)) @Override public void onAttach (Activity activity) (super.onAttach (activity); try (mListener \u003d (OnFragmentInteractionListener) activity;) catch (ClassCastException e) (throw new ClassCastException (activity.toString () + "must implement OnFragmentInteractionListener");)) @Override public void onDetach () (super.onDetach (); mListener \u003d null;) public interface OnFragmentInteractionListener (public void onFragmentInteraction (Uri uri);))

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

Створити хороший UI складно, особливо якщо у тебе ще не так багато досвіду в цій галузі. Тому ось тобі швидкий тест на знання питання: якщо ти звик, що для нового вікна обов'язково потрібен Activity або замість плавної анімації в свіжонаписані програмі чомусь відбуваються конвульсії, - ця стаття для тебе 🙂

граблі Activity

Більшість туторіали, що демонструють фішки Android-розробки, починаються однаково: недосвідченим розробникам пропонують накидати все візуальні елементи прямо в XML-розмітку головного Activity. Виглядає це приблизно так:

Public class MainActivity extends AppCompatActivity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); ...

Така проектування входить в звичку, і проект заповнюється новими Activity зі все більш складною розміткою. Як підсумок - навіть мінімально корисний додаток обростає стеком Activity, зжирає всю оперативну пам'ять, а в розробника летить каміння і двійки в Google Play.

А все тому, що ОС Android абсолютно не обіцяє тримати твої Activity живими. Як ти пам'ятаєш, ці компоненти існують незалежно один від одного, володіючи особливим життєвим циклом. Якщо Activity переходить в стан onPause, а відбувається це досить часто, він стає Котейко Шредінгера: не можна заздалегідь знати, буде він живий чи ні.

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

Крім проблем зі збереженням логіки, є і рутина підтримки ООП-коду: щільно зав'язані на Activity інтерфейси практично неможливо розвивати далі. Масштабування, швидку заміну одних елементів на інші, анімацію - все це буде дуже важко реалізувати в нових версіях програми.

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

Fragments

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

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

Public class FragmentOne extends Fragment (... public View onCreateView (...) (View view \u003d inflater.inflate (R.layout.fragment_one, container, false); TextView textView \u003d (TextView) view.findViewById (R.id. fo_text); textView.setText ( "Hello, i" m first fragment! "); return view; ...

У фрагменті теж можна перевизначати будь-які методи, які відстежують стан вікна. Так, якщо додаток піде в фон, в Activity виконається onPause, а потім метод з точно такою ж назвою виконається тут. Це може бути корисно - зручно для відключення від сторонніх об'єктів, наприклад прив'язаних сервісів (bound service).

FragmentTransaction

Знаючи, що робота з фрагментами буде насиченою, Google завчасно створила для цього спеціальні інструменти. Класи FragmentManager і FragmentTransaction акумулюють в собі всі процеси: створення нових фрагментів і їх видалення, передачу даних і так далі.

Об'єкт FragmentManager створювати не потрібно, він вже є в кожному Activity, потрібно тільки отримати на нього посилання. А всі дії будуть проходити через FragmentTransaction, який потім самостійно передасть дані менеджеру фрагментів.

FragmentManager manager \u003d getSupportFragmentManager (); FragmentTransaction transaction \u003d manager.beginTransaction ();

Хочу зауважити, що класи, що працюють з фрагментами, доступні в двох варіантах. Рекомендую використовувати новішу версію - це ті, у яких в шляху імпорту присутній рядок android.support.v4. Це велика бібліотека, створена для організації забезпечення сумісності. Компанія Google дбайливо ставиться до пристроїв на всіх версіях ОС, а бібліотеки дозволяють використовувати нововведення розробки навіть при роботі зі старим API.

Import android.support.v4.app.FragmentTransaction; import android.support.v4.app.Fragment; ...

FrameLayout

Часто дані в UI приходять динамічно, в залежності від подій, що відбуваються. Щоб було куди помістити картинку або текст, існує спеціальний контейнер - FrameLayout. В цьому і є його основне завдання - зарезервувати на екрані місце для будь-якого об'єкта класу View, який можна буде довантажити пізніше. Фрагменти теж живуть в таких контейнерах.

Додати новий фрагмент в FrameLayout можна по-різному: в FragmentTransaction доступні схожі за функціональністю методи заміни (replace) і додавання (add).

Transaction.replace (R.id.frame_container, fragmentObject);

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

Transaction.add (R.id.frame_container, fragment);

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

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

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

Transaction.commit ();

Продовження доступно тільки учасникам

Варіант 1. Приєднайся до товариства «сайт», щоб читати всі матеріали на сайті

Членство в співтоваристві протягом зазначеного терміну відкриє тобі доступ до ВСІХ матеріалами «Хакера», збільшить особисту накопичувальну знижку і дозволить накопичувати професійний рейтинг Xakep Score!

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

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

Як правило, необхідно реалізувати наступні методи життєвого циклу:

Система викликає цей метод, коли створює фрагмент. У своїй реалізації розробник повинен ініціалізувати ключові компоненти фрагмента, які необхідно зберігати, коли фрагмент знаходиться в стані паузи або відновлений після зупинки. Система викликає цей метод при першому відображенні призначеного для користувача інтерфейсу фрагмента на дисплеї. Для промальовування призначеного для користувача інтерфейсу фрагмента слід повернути з цього методу об'єкт, який є кореневим в макеті фрагмента. Якщо фрагмент не має призначеного для користувача інтерфейсу, можна повернути null. Система викликає цей метод як перша вказівка \u200b\u200bтого, що користувач залишає фрагмент (це не завжди означає знищення фрагмента). Зазвичай саме в цей момент необхідно фіксувати всі зміни, які повинні бути збережені за рамками поточного сеансу роботи користувача (оскільки користувач може не повернутися назад).

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

Відображення переміщуваного діалогового вікна. Використання цього класу для створення діалогового вікна є хорошою альтернативою допоміжних методів діалогового вікна в класі. Справа в тому, що він дає можливість вставити діалогове вікно фрагмента в керований операцією стек переходів назад для фрагментів, що дозволяє користувачеві повернутися до закритого фрагменту. Відображення списку елементів, керованих адаптером (наприклад,), аналогічно класу. Цей клас надає кілька методів для управління списком уявлень, наприклад, метод зворотного виклику для обробки натискань. Відображення ієрархії об'єктів у вигляді списку, аналогічно класу. Цей клас корисний, коли в додатку створюється операція «Налаштування».

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

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

Щоб створити макет для фрагмента, розробник повинен реалізувати метод зворотного виклику, який система Android викликає, коли для фрагмента настає час відобразити свій макет. Реалізація цього методу повинна повертати об'єкт, який є кореневим в макеті фрагмента.

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

Щоб повернути макет з методу, можна виконати його роздування з, певного в XML-файлі. Для цієї мети метод надає об'єкт.

Наприклад, код підкласу класу, що завантажує макет з файлу 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);))

створення макета

У наведеному коді конструкція R.layout.example_fragment є посиланням на ресурс макета на ім'я example_fragment.xml, що зберігається в ресурсах додатка. Докладні відомості про створення макета в XML см. В статті.

Приклад операції, що використовує фрагмент в якості фонового потоку, без користувальницького інтерфейсу, наведено в зразку коду FragmentRetainInstance.java, що входить в число зразків в SDK (і доступному за допомогою Android SDK Manager). Шлях до нього в системі - /APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

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

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

Нижче вказані дії, які дозволяє виконати:

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

Додаткові відомості про ці та інші методи наводяться в документації по класу.

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

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

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

Взаємодія з операцією

public static class FragmentA extends ListFragment (OnArticleSelectedListener mListener; ... @Override public void onAttach (Activity activity) (super.onAttach (activity); try (mListener \u003d (OnArticleSelectedListener) activity;) catch (ClassCastException e) (throw new ClassCastException ( activity.toString () + "must implement OnArticleSelectedListener");)) ...)

Якщо операція не реалізувала інтерфейс, фрагмент генерує виняток. У разі успіху елемент mListener буде містити посилання на реалізацію інтерфейсу OnArticleSelectedListener в операції, щоб фрагмент A міг використовувати події спільно з операцією, викликаючи методи, визначені інтерфейсом OnArticleSelectedListener. Наприклад, якщо фрагмент A є розширенням класу, то всякий раз, коли користувач натискає елемент списку, система викликає у фрагменті. Цей метод, в свою чергу, викликає метод 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 \u003d ContentUris. (ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected (noteUri);) ...)

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

Додаткові відомості про роботу з постачальником контенту наводяться в документі.

Додавання елементів в рядок дій

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

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

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

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

Докладні відомості щодо меню див. У посібниках для розробників і

Управління життєвим циклом фрагмента

малюнок 3. Вплив життєвого циклу операції на життєвий цикл фрагмента

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

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

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

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

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

Увага! Якщо виникне необхідність в об'єкті всередині об'єкта класу, можна викликати метод. Однак розробник повинен бути уважним і викликати метод тільки коли фрагмент прикріплений до операції. Якщо фрагмент ще не прикріплений або був відкріплений в кінці його життєвого циклу, метод поверне null.

Узгодження з життєвим циклом операції

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

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

OnCreate (), фрагмент всередині цієї операції приймає всього лише метод зворотного виклику.

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

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

приклад:

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

Примітка. Повний вихідний код цієї операції знаходиться в розділі.

Головна операція застосовує макет звичайним способом, в методі:

@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); setContentView (R.layout.fragment_layout);)

Тут застосовується макет fragment_layout.xml:

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

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

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

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

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

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

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

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

Public static class DetailsActivity extends Activity (@Override protected void onCreate (Bundle savedInstanceState) (super.onCreate (savedInstanceState); if (getResources (). GetConfiguration (). Orientation \u003d\u003d Configuration.ORIENTATION_LANDSCAPE) (// If the screen is now in landscape mode, we can show the // dialog in-line with the list so we don "t need this activity. finish (); return;) if (savedInstanceState \u003d\u003d null) (// During initial setup, plug in the details fragment. DetailsFragment details \u003d new DetailsFragment (); details.setArguments (getIntent (). getExtras ()); getFragmentManager (). beginTransaction (). add (android.R.id.content, details) .commit ();)) )

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

Додаткові зразки коду, що використовує фрагменти (і файли з повним вихідним кодом цього прикладу), доступні в додатку-прикладі API Demos в розділі (доступного для завантаження).

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


Список перехоплюваних методів фрагмента

Три основні методи, які використовуються практично в будь-якому додатку:

onCreate - ініціалізація внутрішніх компонентів фрагмента збереженими даними.

onCreateView - формування компонента для відображення.

onPause - обробка ситуації, коли фрагмент втрачає фокус.

Опис усіх методів по порядку їх виклику:

onAttach - перший під'єднання фрагмента до активності. Сюди передається активність, до якої відбувається приєднання.

onCreate - ініціалізація фрагмента. Сюди передаються дані класу Bundle про останньому стані фрагмента в його попередньому житті, якщо така була, збережені раніше, наприклад, в методі onSaveInstanceState, для відновлення цього стану. На цей момент активність ще знаходиться в процесі створення.

onCreateView - формування образу для відображення. Повертає вид фрагмента. може повертатиnull для невізуальних компонентів. Сюди передаються дані класу Bundle про останньому стані фрагмента, а також контейнер активності, куди буде підключатися фрагмент і «надуватель» розмітки.

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

onActivityCreated - остаточна ініціалізація. Визивет коли метод onCreate () активності був повернутий. Активність створена, фрагмент в неї вставлений. Використовується, наприклад, для відновлення стану фрагмента. Сюди передаються дані класу Bundle про останньому стані фрагмента.

onViewStateRestored - ініціалізація виду на основі збереженого стану. Викликається, коли збережене стан виду відновлено.

onSaveInstanceState - збереження стану фрагмента. Спрацьовує тільки в тому випадку, якщо фрагмент зупиняється і може бути убитий системою, але фактично ще потрібен. Це відбувається, наприклад, при виклику наступної активності, при натисканні кнопки «додому» а також у разі повного руйнування активності і створення її заново в результаті зміни конфігурації пристрою (зміна мови, пристрої введення, орієнтації екрану і т.п.). Об'єкт класу Bundle, який зберігає стан активності, передається в методи onCreate, onPostCreate і onRestoreInstanceState. Увага! Метод може бути викликаний в будь-який час доonDestroy!

onDestroyView - викликається при від'єднанні виду від фрагмента. При наступному відображенні фрагмента буде сформований новий вид.

onDetach - викликається перед тим, як виймати фрагмента від активності.