Использование ActionBar Up Навигация в фрагментах мастера/детали

У меня есть приложение с макетом/макетом (1 активность, 1 фрагмент ListView и 1 фрагмент детали). Когда пользователь нажимает элемент в ListView, транзакция фрагмента создает фрагмент детали на правой панели, который содержит информацию, соответствующую этому элементу. Когда показан фрагмент детали, я скрываю кнопки/элементы начальных кнопок действий и показываю 3 новых элемента AB (done/delete/cancel). Пользователь может очистить правую панель и вернуться в исходное состояние пользовательского интерфейса, нажав кнопку "Назад" или нажав один из трех элементов AB.

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

XML-макет для действия следующий (внутри LinearLayout; prettify скрывает эту строку):   

ПодробностиFragement содержит оператор actionBar.setDisplayHomeAsUpEnabled в методе onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setHasOptionsMenu(true);
 ActionBar actionBar = getSherlockActivity().getSupportActionBar();
 actionBar.setDisplayHomeAsUpEnabled(true);
}

Для фрагмента ListView и фрагментов Detail в фрагментах реализованы метод onCreateOptionsMenu() и onOptionsItemSelected(). Ниже кода фрагмента детали:

@Override
public void onCreateOptionsMenu(Menu menu, ************ inflater) {
 inflater.inflate(R.menu.edit_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
 // some variable statements...
 switch (item.getItemId()) {
 case android.R.id.home:
 //Toast.makeText(getSherlockActivity(), "Tapped home", Toast.LENGTH_SHORT).show();
 onHomeSelectedListener.onHomeSelected();
 return true;
 case R.id.menu_edit_item_done:
 editedTask.setTitle(editedTaskTitle);
 onTaskEditedListener.onTaskEdited(editedTask, UPDATE_TASK, true);
 return true;
 default:
 return super.onOptionsItemSelected(item);
 }
}

В действии хоста я реализую onHomeSelectedListner для обработки значка на домашней странице приложения (то есть "вверх" ):

public void onHomeSelected(){
 FragmentManager manager = getSupportFragmentManager();
 FragmentTransaction ft = manager.beginTransaction();
 TaskFragment taskFragment = (TaskFragment)getSupportFragmentManager().findFragmentById(R.id.details);
 ft.remove(taskFragment);
 ft.commit();
 manager.popBackStack();
}

Слушатель активности, которому поручено обрабатывать все другие кнопки панели действий (т.е. done/delete/cancel), является onTaskEditedListener, и помимо другого кода, который обрабатывает некоторые данные, он имеет те же операции с фрагментами, которые показаны выше.

Обновить (1/24) Основываясь на обратной связи tyczj и straya, я разместил операторы журнала внутри onCreate(), onResume(), onPause() для определения различий между onHomeSelected и onTaskEdited слушателями. Я могу подтвердить, что во время события "вверх навигации" (т.е. OnHomeSelected) onPause() вызывается onCreate() и onResume(). В то время как во время вызова onTaskEdited (т.е. Кнопки возврата или завершения/удаления/отмены) ни одно из этих событий не вызывается.

Обновление (1/25) Основываясь на предположении Марка Мерфи, я прокомментировал вызов метода onHomeSelected в инструкции case case.R.id.home, чтобы посмотреть, что будет делать Activity. Мысль заключалась в том, что приложение ничего не сделало бы с тех пор, пока не будет никаких утверждений. Оказывается, это не так. Даже без вызова метода слушателя (то есть, который удаляет фрагмент), активность перезапускается и фрагмент детали удаляется из контейнера фрагментов.

Обновление (2/28) Я временно обманывал тот факт, что мое основное действие было перезапущено, отключив анимацию окна (как указано в моем собственном ответе). Однако после дальнейшего тестирования я обнаружил ошибку. Благодаря образцу кода Вольфрама Риттмейера я смог выяснить истинную причину (причины), почему моя активность была перезапущена (в макете/подробном одиночном макете) во время навигации: 1) Хотя я использовал этот "onHomeSelectedListener" для правильного удаления фрагмента из backstack, у меня все еще был некоторый остаток кода в элементе ListView onOptionsItemSelected, который создавал новое намерение начать хостинг. Вот почему нажатие на значок приложения домой возобновило действие. 2) В моей последней реализации (показано в моем собственном ответе) я избавился от onHomeSelectedListener в действии и заменил цель startActivity (т.е. код нарушения) внутри ListView onOptionsItemSelected, чтобы использовать удаление фрагмента + код popBackStack изначально в onHomeSelectedListener.

4 ответа

После долгих исследований и опрокидывания выяснилось, что только причина, по которой моя активность перезапускалась во время навигации вверх для конфигурации master/detail, заключалась в том, что я оставил некоторый код в элементе ListView Fragment onOptionsItemSelected, который создавал намерение запустить главную в дополнение к моему коду транзакции с полным фрагментом в другом месте. Ниже приведена окончательная реализация, с которой я получил "навигацию вверх" для правильной работы на обеих телефонах (несколько видов деятельности) и планшетных (однопроцессорных/многопанельных) конфигурациях. Благодаря Wolfram Rittmeyer за пару советов в его коде (ссылка в разделе комментариев), которые помогут мне определить мою проблему!

Основная активность: Хост фрагментов и выполнение некоторых других операций, специфичных для приложения

Фрагмент списка ListView: Ручки "вверх по навигации в конфигурации таблицы

@Override
public boolean onOptionsItemSelected(MenuItem item) {
 switch (item.getItemId()) {
 case android.R.id.home:
 if(mDualPane){
 FragmentManager manager = getSherlockActivity().getSupportFragmentManager();
 FragmentTransaction ft = manager.beginTransaction();
 DetailFragment detailFragment = (DetailFragment)manager.findFragmentById(R.id.details);
 ft.remove(detailFragment);
 ft.commit();
 manager.popBackStack();
 getSherlockActivity().getSupportActionBar().setDisplayHomeAsUpEnabled(false);
 getSherlockActivity().getSupportActionBar().setHomeButtonEnabled(false);
 }
 return true;
 // Other case statements...
 default:
 return super.onOptionsItemSelected(item);
 }
}

Подробнее Фрагмент: Управляет навигацией в конфигурации телефона

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setHasOptionsMenu(true);
 // Sets "up navigation" for both phone/tablet configurations
 ActionBar actionBar = getSherlockActivity().getSupportActionBar();
 actionBar.setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
 switch (item.getItemId()) {
 case android.R.id.home:
 if(!mDualPane){
 Intent ******************** = new Intent(getSherlockActivity(), MainActivity.class);
 ********************.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
 startActivity(********************);
 getSherlockActivity().finish();
 }
 return true;
 // Other case statements...
 default:
 return super.onOptionsItemSelected(item);
 }
}


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

Итак, скажите, что у вас есть 2 действия, называя их A1 и A2. Нажатие на что-то в A1 приведет вас к A2. Если пользователь нажал кнопку "домой", вы должны вернуть их в A1, очистив стек всего до тех пор, пока эта активность не будет выглядеть как

Intent intent = new Intent(this, A1.class); 
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

это то, что делает флаг Intent.FLAG_ACTIVITY_CLEAR_TOP

Если установлено, и запущенная деятельность уже запущена в текущей задаче, вместо того, чтобы запускать новый экземпляр этого действия, все остальные действия поверх него будут закрыты, и это намерение будет доставлено в (сейчас сверху) старой деятельности как нового намерения.

Например, рассмотрите задачу, состоящую из действий: A, B, C, D. Если D вызывает startActivity() с намерением, который разрешает компонент активности B, то C и D будут завершены и B получит данное намерение, в результате чего стек теперь будет: A, B.`

Текущий исполняемый экземпляр действия B в приведенном выше примере либо получит новое намерение, которое вы начинаете здесь, в методе onNewIntent(), либо сам закончите и перезапустите с новым намерением. Если он объявил свой режим запуска "множественным" (по умолчанию), и вы не установили FLAG_ACTIVITY_SINGLE_TOP в одном и том же намерении, он будет завершен и заново создан; для всех других режимов запуска или если установлен FLAG_ACTIVITY_SINGLE_TOP, этот Intent будет доставлен в текущий экземпляр onNewIntent().

Этот режим запуска также можно использовать для хорошего эффекта в сочетании с FLAG_ACTIVITY_NEW_TASK: если он используется для запуска корневой активности задачи, он приведет к запуску текущего исполняемого экземпляра этой задачи на передний план, а затем очистит его до корня государство. Это особенно полезно, например, при запуске активности из диспетчера уведомлений.


не: break и затем возвращать super.onOptionsItemSelected(item), а просто: return true;

UPDATE:

Итак, вы говорите, что активность "перезапущена" на основе того, что вы видите в "Представлениях", но можете ли вы подтвердить, что может или не может произойти с Activity (и фрагментами, если на то пошло), используя ведение журнала в различном жизненном цикле методы? Таким образом, вы можете быть уверены в том, каково текущее (ошибочное) поведение, прежде чем двигаться вперед с диагнозом.

UPDATE:

Хорошо, хорошо, чтобы быть уверенным в поведении:) Теперь о вашем вопросе "Каков правильный способ реализации" навигации вверх "для макета/макета (1 активность /2 фрагмента)?": Типичным способом является то, что 2 фрагмента добавлены в один фрагмент FragmentTransaction, и вы просто popBackStack, чтобы удалить их и вернуться к тому, что было в предыдущем состоянии. Я думаю, что вы удваиваете, вручную удаляя фрагмент внутри FragmentTransaction, а затем выскакивая назад. Попробуйте просто popBackStack. О, и просто быть уверенным и последовательным, поскольку вы используете ActionBarSherlock и support.v4, вы используете FragmentActivity (а не Activity) и SherlockFragment?


Я думаю, что вы должны обрабатывать кнопку "Вверх" только внутри этой операции. Если вы находитесь в телефоне, кнопка "вверх" будет обрабатываться действием, которое действует как обертка этого фрагмента, в планшете (шаблон мастера/детали), который вы вообще не хотите.

licensed under cc by-sa 3.0 with attribution.