Как работает Меню в фрагментах

Хорошим стилем программирования в Google считают использование ActionBar и Фрагментов.

Для этого они даже изменили конструктор нового проекта в Eclipse .

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

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

Сейчас уже сложилась целая традиция в написании кода, чтобы потом не восклицать «какой идиот это написал?»  «как это вообще работает?», я вывожу общий функционал в конкретный класс. Например: класс для работы с базой данных, класс для работы с железом устройства, класс для работы с интернетом.

Так и каждый фрагмент храню отдельным классом унаследованным от чистого Fragment. А когда надо,  создаю его через newInstance.

К примеру вот так :

[cce lang=»java» tab_size=»2″ no_links=»false»]

Fragment f = AddCitationFragment.newInstance(R.string.Citation_edit, true);
FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, f);
ft.addToBackStack(null);
ft.commit();

[/cce]

А внутри его задаю нужные свойства:

[cce lang=»java» tab_size=»2″ no_links=»false»]

static AddCitationFragment newInstance(int galerytype, boolean isEdit) {
AddCitationFragment f = new AddCitationFragment();
Bundle arguments = new Bundle();
arguments.putInt(GALLERY_TYPE, galerytype);
arguments.putBoolean(EDIT, isEdit);
f.setArguments(arguments);
f.setHasOptionsMenu(true);
return f;
}

[/cce]

В Bundle мы закладываем разные данные, нужные по ходу жизни фрагмента. А вот свойство setHasOptionsMenu(true) отвечает за наличие собственного меню в ActionBar  этого фрагмента. Для этого нужно переопределить onCreateOptionsMenu у данного фрагмента.

[cce lang=»java» tab_size=»2″ no_links=»false»]

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.add_citation, menu);
super.onCreateOptionsMenu(menu, inflater);
}

[/cce]

А при вызове этого меню нужно переопределить onOptionsItemSelected у данного фрагмента:

[cce lang=»java» tab_size=»2″ no_links=»false»]

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.save_citation:
Save_Citation();
return true;
}
return false;
}

[/cce]

На этом можно возрадоваться и успокоиться, но только гладко это все в теории, а на практике не у каждого фрагмента отработает onOptionsItemSelected, как и задумано. В частности, два дня было потрачено на осознание, почему в одном и том же фрагменте onOptionsItemSelected,  в одном случае вызывается, а в другом нет.

Сначала данный метод отработает в главной активности, подробно товарищи расписали этот механизм здесь, и если не нашлось в нем нужного действия, вернуть он должен  false. Далее вызовется onOptionsItemSelected   вашего фрагмента и в нем нужно искать и вызывать нужное действие. Также важно следить за тем, какие фрагменты и в какой последовательности вы создаете, т.к. есть фрагменты, которые как бы и не активны, а перехватывают ваш метод  onOptionsItemSelected  у себя, и у конечного фрагмента он не вызовется. Таким, к примеру, является ViewPagerFragment, именно он отнял у меня  два дня, которые были проведены с отладчиком и поисковиком. Решений у данной проблемы может быть несколько, например, можно добавить в onOptionsItemSelected  самого пейджера нужное нам действие, но тогда страдает архитектура классов, т.к. реализация действия одного фрагмента будет находиться в другом. Другой метод состоит в том, чтобы вычистить стек транзакций от злополучного фрагмента и тогда все действия будут в одном классе, но правда тогда придется более детально продумывать всю очередь фрагментов.Или можно просто в каждом onOptionsItemSelected возвращать false, если мы не нашли в нём нужного нам действия.

Нашли ошибку в тексте?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *