Передать данные из фрагмента в фрагмент. Фрагменты. Добавление элементов в строку действий

Передать данные из фрагмента в фрагмент. Фрагменты. Добавление элементов в строку действий

02.03.2019

Мы затронули тему перехода между Activity в Android-приложениях. И, в частности, обсуждали передачу данных из одного Activity в другое. Очевидное решение intent.putExtra(NextActivity.FIELD_NAME, field_value) хорошо служит нам до тех пор, пока мы передаём данные "простых" типов: String, int, long или массивы. Но стоит нам попытаться передать таким образом объект из нашей модели данных, начинаются трудности.
Объекты таким способом передавать можно, но они должны реализовывать интерфейс Serializable или Parcelable. Первый вариант по некоторым найденным мной отзывам, снижает производительность приложения. Второй - давайте рассмотрим внимательнее.
Вот пример класса одного из моих приложений:

  1. public class Email implements Parcelable {
  2. private String address;
  3. private String type;
  4. private Email(Parcel in ) {
  5. this .address = in .readString();
  6. this .type = in .readString();
  7. public String getAddress() {
  8. return address;
  9. public void setAddress(String address) {
  10. this .address = address;
  11. public String getType() {
  12. return type;
  13. public void setType(String t) {
  14. this .type = t;
  15. public Email(String a, String t) {
  16. this .address = a;
  17. this .type = t;
  18. public int describeContents() {
  19. return 0;
  20. public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
  21. public Email createFromParcel(Parcel in ) {
  22. return new Email(in );
  23. public Email newArray(int size) {
  24. return new Email;
  25. public void writeToParcel(Parcel parcel, int i) {
  26. parcel.writeString(address);
  27. parcel.writeString(type);
Как видим,он реализует интерфейс Parcelable, а значит может легко передаваться между Activity. Как мы этого добиваемся? Мы дополняем логику класса методами для его Parcel-изации и Де-parcel-изации, проще говоря, сохранения в Parcel и восстановления из него. Parcel тут можно рассматривать как некий буфер, в который можно сложить в определённом порядке данные любых типов и затем (в том же порядке!) их оттуда извлечь.
Складывать содержимое полей класса в Parcel очень просто. Это реализуется в методе writeToParcel . Для восстановления объекта действуем так: Создаём вложенный класс Parcelable.Creator , в котором реализуем два метода. Второй выглядит всегда одинаково, а вот первый для нас весьма важен: он вызовет новый конструктор нашего класса, передавая в него Parcel. В конструкторе мы должны реализовать логику, обратную методу writeToParcel , т.е. вычитать из Parcel-а значения полей класса в том же порядке, в каком их туда записывали. В случае простых полей типа String это несложно. Если же у нас есть поля типа ArrayList, то делаем так:
p.writeInt(phone.size());
for (int i = 0; i < phone.size(); i++) {
p.writeString(phone.get (i));
}
для сохранения, и
int count = p.readInt();
for (int i = 0; i < count; i++) {
String ph = p.readString();
phone.add(ph);
}
для восстановления. Тут p, как вы, наверное, догадались - объект класса Parcel, а phone - поле типа ArrayList.
Если полями нашего класса являются другие наши классы, то мы должны их также "научить Parcel-изоваться". Тогда поля таких типов мы будем укладывать в Parcel методом
p .writeParcelable(organization, Parcelable.CONTENTS_FILE_DESCRIPTOR);
и извлекать оттуда методом
organization = p .readParcelable(getClass().getClassLoader());

Рисунок 2. Жизненный цикл фрагмента (во время выполнения операции)

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

Как правило, необходимо реализовать следующие методы жизненного цикла:

Система вызывает этот метод, когда создает фрагмент. В своей реализации разработчик должен инициализировать ключевые компоненты фрагмента, которые требуется сохранить, когда фрагмент находится в состоянии паузы или возобновлен после остановки. Система вызывает этот метод при первом отображении пользовательского интерфейса фрагмента на дисплее. Для прорисовки пользовательского интерфейса фрагмента следует возвратить из этого метода объект , который является корневым в макете фрагмента. Если фрагмент не имеет пользовательского интерфейса, можно возвратить null. Система вызывает этот метод как первое указание того, что пользователь покидает фрагмент (это не всегда означает уничтожение фрагмента). Обычно именно в этот момент необходимо фиксировать все изменения, которые должны быть сохранены за рамками текущего сеанса работы пользователя (поскольку пользователь может не вернуться назад).

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

Отображение перемещаемого диалогового окна. Использование этого класса для создания диалогового окна является хорошей альтернативой вспомогательным методам диалогового окна в классе . Дело в том, что он дает возможность вставить диалоговое окно фрагмента в управляемый операцией стек переходов назад для фрагментов, что позволяет пользователю вернуться к закрытому фрагменту. Отображение списка элементов, управляемых адаптером (например, ), аналогично классу . Этот класс предоставляет несколько методов для управления списком представлений, например, метод обратного вызова для обработки нажатий. Отображение иерархии объектов в виде списка, аналогично классу . Этот класс полезен, когда в приложении создается операция «Настройки».

Добавление пользовательского интерфейса

Фрагмент обычно используется как часть пользовательского интерфейса операции, при этом он добавляет в операцию свой макет.

Чтобы создать макет для фрагмента, разработчик должен реализовать метод обратного вызова , который система 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 = (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 = 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 == 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 == null) { // During initial setup, plug in the details fragment. DetailsFragment details = new DetailsFragment(); details.setArguments(getIntent().getExtras()); getFragmentManager().beginTransaction().add(android.R.id.content, details).commit(); } } }

Обратите внимание, что в альбомной конфигурации эта операция самостоятельно завершается, чтобы главная операция могла принять управление и отобразить фрагмент DetailsFragment рядом с фрагментом TitlesFragment . Это может произойти, если пользователь запустит операцию DetailsActivity в книжной ориентации экрана, а затем перевернет устройство в альбомную ориентацию (в результате чего текущая операция будет перезапущена).

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



© 2024 beasthackerz.ru - Браузеры. Аудио. Жесткий диск. Программы. Локальная сеть. Windows