Android 4.2: поведение обратного стека с вложенными фрагментами
С Android 4.2 библиотека поддержки получила поддержку вложенных фрагментов здесь. Я общался с ним и нашел интересное поведение/ошибку в отношении заднего стека и getChildFragmentManager(). При использовании getChildFragmentManager() и addToBackStack (String name), нажав кнопку "Назад", система не запускает задний стек до предыдущего фрагмента.
С другой стороны, при использовании getFragmentManager() и addToBackStack (имя строки), нажав кнопку "Назад", система вернется к предыдущему фрагменту.
Для меня это поведение неожиданно. Нажимая кнопку "Назад" на моем устройстве, я ожидаю, что последний добавленный фрагмент в задний стек будет всплывать, даже если фрагмент был добавлен в задний стек в диспетчере фрагментов для детей.
Правильно ли это поведение? Это ошибка? Есть ли проблема в этой проблеме?
пример кода с помощью getChildFragmentManager():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getChildFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
пример кода с getFragmentManager():
public class FragmentceptionActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
final FrameLayout wrapper1 = new FrameLayout(this);
wrapper1.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper1.setId(1);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 0;
final TextView text = new TextView(this);
text.setLayoutParams(params);
text.setText("fragment 1");
wrapper1.addView(text);
setContentView(wrapper1);
getSupportFragmentManager().beginTransaction().addToBackStack(null)
.add(1, new Fragment1()).commit();
}
public class Fragment1 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper2 = new FrameLayout(getActivity());
wrapper2.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper2.setId(2);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 100;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 2");
wrapper2.addView(text);
return wrapper2;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(2, new Fragment2()).commit();
}
}
public class Fragment2 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper3 = new FrameLayout(getActivity());
wrapper3.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper3.setId(3);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 200;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 3");
wrapper3.addView(text);
return wrapper3;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(3, new Fragment3()).commit();
}
}
public class Fragment3 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper4 = new FrameLayout(getActivity());
wrapper4.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper4.setId(4);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 300;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 4");
wrapper4.addView(text);
return wrapper4;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
getFragmentManager().beginTransaction().addToBackStack(null)
.add(4, new Fragment4()).commit();
}
}
public class Fragment4 extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final FrameLayout wrapper5 = new FrameLayout(getActivity());
wrapper5.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT));
wrapper5.setId(5);
final FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT);
params.topMargin = 400;
final TextView text = new TextView(getActivity());
text.setLayoutParams(params);
text.setText("fragment 5");
wrapper5.addView(text);
return wrapper5;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
}
Ответы
Ответ 1
Похоже на ошибку.
Взгляни на:
http://code.google.com/p/android/issues/detail?id=40323
Обходной путь, который я использовал успешно (как указано в комментариях):
@Override
public void onBackPressed() {
// If the fragment exists and has some back-stack entry
if (mActivityDirectFragment != null && mActivityDirectFragment.getChildFragmentManager().getBackStackEntryCount() > 0){
// Get the fragment fragment manager - and pop the backstack
mActivityDirectFragment.getChildFragmentManager().popBackStack();
}
// Else, nothing in the direct fragment back stack
else{
// Let super handle the back press
super.onBackPressed();
}
}
Ответ 2
Это решение может быть лучшей версией ответа @Sean:
@Override
public void onBackPressed() {
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
FragmentManager fm = getSupportFragmentManager();
for (Fragment frag : fm.getFragments()) {
if (frag.isVisible()) {
FragmentManager childFm = frag.getChildFragmentManager();
if (childFm.getBackStackEntryCount() > 0) {
childFm.popBackStack();
return;
}
}
}
super.onBackPressed();
}
Снова, я подготовил это решение на основе ответа @Sean выше.
Как сказал @AZ13, это решение возможно только в ситуациях с одним фрагментом детского фрагмента. В случае фрагментов с несколькими уровнями работы работы становятся немного сложными, поэтому я рекомендую попробовать это решение только в случае, когда я это сказал. =)
Примечание: Поскольку метод getFragments
теперь является приватным методом, это решение не будет работать. Вы можете проверить комментарии для ссылки, которая предлагает решение об этой ситуации.
Ответ 3
Это решение может быть лучшей версией ответа @ismailarilik:
Версия с вложенным фрагментом
private boolean onBackPressed(FragmentManager fm) {
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
if (onBackPressed(frag.getChildFragmentManager())) {
return true;
}
}
}
}
}
return false;
}
@Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (onBackPressed(fm)) {
return;
}
super.onBackPressed();
}
Ответ 4
С помощью этого ответа он будет обрабатывать рекурсивную обратную проверку и дать каждому фрагменту шанс переопределить поведение по умолчанию. Это означает, что вы можете иметь фрагмент, на котором размещается ViewPager, сделать что-то особенное, например, прокручивать страницу, которая в качестве заднего стека, или прокручиваться до главной страницы, а затем на следующем обратном нажатии на выход.
Добавьте это в свою деятельность, которая расширяет AppCompatActivity.
@Override
public void onBackPressed()
{
if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
super.onBackPressed();
}
}
Добавьте это в свой BaseFragment или класс, на который вы можете наследовать все ваши фрагменты.
public static boolean handleBackPressed(FragmentManager fm)
{
if(fm.getFragments() != null){
for(Fragment frag : fm.getFragments()){
if(frag != null && frag.isVisible() && frag instanceof BaseFragment){
if(((BaseFragment)frag).onBackPressed()){
return true;
}
}
}
}
return false;
}
protected boolean onBackPressed()
{
FragmentManager fm = getChildFragmentManager();
if(handleBackPressed(fm)){
return true;
}
else if(getUserVisibleHint() && fm.getBackStackEntryCount() > 0){
fm.popBackStack();
return true;
}
return false;
}
Ответ 5
Реальный ответ на этот вопрос находится в функции транзакции фрагмента, которая называется setPrimaryNavigationFragment.
/**
* Set a currently active fragment in this FragmentManager as the primary navigation fragment.
*
* <p>The primary navigation fragment's
* {@link Fragment#getChildFragmentManager() child FragmentManager} will be called first
* to process delegated navigation actions such as {@link FragmentManager#popBackStack()}
* if no ID or transaction name is provided to pop to. Navigation operations outside of the
* fragment system may choose to delegate those actions to the primary navigation fragment
* as returned by {@link FragmentManager#getPrimaryNavigationFragment()}.</p>
*
* <p>The fragment provided must currently be added to the FragmentManager to be set as
* a primary navigation fragment, or previously added as part of this transaction.</p>
*
* @param fragment the fragment to set as the primary navigation fragment
* @return the same FragmentTransaction instance
*/
public abstract FragmentTransaction setPrimaryNavigationFragment(Fragment fragment);
Вы должны установить эту функцию на начальный родительский фрагмент, когда действие добавляет его. У меня есть функция replaceFragment внутри моей деятельности, которая выглядит следующим образом:
public void replaceFragment(int containerId, BaseFragment fragment, boolean addToBackstack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setPrimaryNavigationFragment(fragment);
if (addToBackstack) {
fragmentTransaction.addToBackStack(fragment.TAG);
}
fragmentTransaction.replace(containerId, fragment).commit();
}
Это дает вам такое же поведение, как если бы вы вернулись с обычного фрагмента B обратно к фрагменту A, за исключением того, что теперь это касается и дочерних фрагментов!
Ответ 6
Причина в том, что ваша активность происходит от FragmentActivity, которая обрабатывает нажатие клавиши BACK (см. строку 173 FragmentActivity.
В нашем приложении я использую ViewPager (с фрагментами), и каждый фрагмент может иметь вложенные фрагменты. Как я справился с этим:
- определение интерфейса OnBackKeyPressedListener одним методом
void onBackKeyPressed()
- реализовал этот интерфейс в "верхних" фрагментах, которые ViewPager показывает
- переопределение onKeyDown и обнаружение BACK нажмите и вызывают onBackKeyPressed в текущем активном фрагменте в представлении пейджера.
Также обратите внимание, что я использую getChildFragmentManager()
в фрагментах для правильного размещения фрагментов. Вы можете увидеть обсуждение и объяснение в этом сообществе разработчиков Android.
Ответ 7
Этот код будет перемещаться по дереву менеджеров фрагментов и возвращать последний добавленный, который имеет любые фрагменты, которые он может выскочить из стека:
private FragmentManager getLastFragmentManagerWithBack(FragmentManager fm)
{
FragmentManager fmLast = fm;
List<Fragment> fragments = fm.getFragments();
for (Fragment f : fragments)
{
if ((f.getChildFragmentManager() != null) && (f.getChildFragmentManager().getBackStackEntryCount() > 0))
{
fmLast = f.getFragmentManager();
FragmentManager fmChild = getLastFragmentManagerWithBack(f.getChildFragmentManager());
if (fmChild != fmLast)
fmLast = fmChild;
}
}
return fmLast;
}
Вызвать метод:
@Override
public void onBackPressed()
{
FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());
if (fm.getBackStackEntryCount() > 0)
{
fm.popBackStack();
return;
}
super.onBackPressed();
}
Ответ 8
i смог обработать фрагмент назад, добавив родительскому фрагменту этот метод в метод onCreate View() и передав корневой вид.
private void catchBackEvent(View v){
v.setFocusableInTouchMode(true);
v.requestFocus();
v.setOnKeyListener( new OnKeyListener()
{
@Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
if(isEnableFragmentBackStack()){
getChildFragmentManager().popBackStack();
setEnableFragmentBackStack(false);
return true;
}
else
return false;
}
return false;
}
} );
}
Метод isEnableFragmentBackStack() - это логический флаг, который должен знать, когда я на основном фрагменте или следующем.
Убедитесь, что, когда вы фиксируете фрагмент, который должен иметь стек, вы должны добавить метод addToBackstack.
Ответ 9
Это решение может быть лучше, поскольку оно проверяет все уровни вложенных фрагментов:
/**
* This method will go check all the back stacks of the added fragments and their nested fragments
* to the the {@code FragmentManager} passed as parameter.
* If there is a fragment and the back stack of this fragment is not empty,
* then emulate 'onBackPressed' behaviour, because in default, it is not working.
*
* @param fm the fragment manager to which we will try to dispatch the back pressed event.
* @return {@code true} if the onBackPressed event was consumed by a child fragment, otherwise {@code false}.
*/
public static boolean dispatchOnBackPressedToFragments(FragmentManager fm) {
List<Fragment> fragments = fm.getFragments();
boolean result;
if (fragments != null && !fragments.isEmpty()) {
for (Fragment frag : fragments) {
if (frag != null && frag.isAdded() && frag.getChildFragmentManager() != null) {
// go to the next level of child fragments.
result = dispatchOnBackPressedToFragments(frag.getChildFragmentManager());
if (result) return true;
}
}
}
// if the back stack is not empty then we pop the last transaction.
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
fm.executePendingTransactions();
return true;
}
return false;
}
в вашей деятельности onBackPressed
вы можете просто называть это следующим образом:
FragmentManager fm = getSupportFragmentManager();
// if there is a fragment and the back stack of this fragment is not empty,
// then emulate 'onBackPressed' behaviour, because in default, it is not working
if (!dispatchOnBackPressedToFragments(fm)) {
// if no child fragment consumed the onBackPressed event,
// we execute the default behaviour.
super.onBackPressed();
}
Ответ 10
Спасибо всем за помощь, эта (измененная версия) работает для меня:
@Override
public void onBackPressed() {
if (!recursivePopBackStack(getSupportFragmentManager())) {
super.onBackPressed();
}
}
/**
* Recursively look through nested fragments for a backstack entry to pop
* @return: true if a pop was performed
*/
public static boolean recursivePopBackStack(FragmentManager fragmentManager) {
if (fragmentManager.getFragments() != null) {
for (Fragment fragment : fragmentManager.getFragments()) {
if (fragment != null && fragment.isVisible()) {
boolean popped = recursivePopBackStack(fragment.getChildFragmentManager());
if (popped) {
return true;
}
}
}
}
if (fragmentManager.getBackStackEntryCount() > 0) {
fragmentManager.popBackStack();
return true;
}
return false;
}
ПРИМЕЧАНИЕ. Вероятно, вы также захотите установить цвет фона этих вложенных фрагментов на цвет фона окна темы приложения, поскольку по умолчанию они прозрачны. В некоторой степени вне сферы действия этого вопроса, но это достигается путем разрешения атрибута android.R.attr.windowBackground и установки фона фрагмента для этого идентификатора ресурса.
Ответ 11
Более 5 лет и этот вопрос по-прежнему актуален. Если вы не хотите использовать фрагментManager.getFragments() из-за его ограничения. Расширьте и используйте следующие классы:
NestedFragmentActivity.java
abstract public class NestedFragmentActivity extends AppCompatActivity {
private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
private final Stack<String> mActiveFragmentTagStack = new Stack<>();
@Override
public void onBackPressed() {
if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {
// Find by id
int lastFragmentId = mActiveFragmentIdStack.lastElement();
NestedFragment nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentById(lastFragmentId);
// If cannot find by id, find by tag
if (nestedFragment == null) {
String lastFragmentTag = mActiveFragmentTagStack.lastElement();
nestedFragment = (NestedFragment) getSupportFragmentManager().findFragmentByTag(lastFragmentTag);
}
if (nestedFragment != null) {
nestedFragment.onBackPressed();
}
// If cannot find by tag, then simply pop
mActiveFragmentTagStack.pop();
mActiveFragmentIdStack.pop();
} else {
super.onBackPressed();
}
}
public void addToBackStack(int fragmentId, String fragmentTag) {
mActiveFragmentIdStack.add(fragmentId);
mActiveFragmentTagStack.add(fragmentTag);
}
}
NestedFragment.java
abstract public class NestedFragment extends Fragment {
private final Stack<Integer> mActiveFragmentIdStack = new Stack<>();
private final Stack<String> mActiveFragmentTagStack = new Stack<>();
private NestedFragmentActivity mParentActivity;
private NestedFragment mParentFragment;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (getParentFragment() == null) {
try {
mParentActivity = (NestedFragmentActivity) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + NestedFragmentActivity.class.getName());
}
} else {
try {
mParentFragment = (NestedFragment) getParentFragment();
} catch (ClassCastException e) {
throw new ClassCastException(getParentFragment().getClass().toString()
+ " must implement " + NestedFragment.class.getName());
}
}
}
public void onBackPressed() {
if (mActiveFragmentIdStack.size() > 0 && mActiveFragmentTagStack.size() > 0) {
// Find by id
int lastFragmentId = mActiveFragmentIdStack.lastElement();
NestedFragment nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentById(lastFragmentId);
// If cannot find by id, find by tag
if (nestedFragment == null) {
String lastFragmentTag = mActiveFragmentTagStack.lastElement();
nestedFragment = (NestedFragment) getChildFragmentManager().findFragmentByTag(lastFragmentTag);
}
if (nestedFragment != null) {
nestedFragment.onBackPressed();
}
// If cannot find by tag, then simply pop
mActiveFragmentIdStack.pop();
mActiveFragmentTagStack.pop();
} else {
getChildFragmentManager().popBackStack();
}
}
private void addToBackStack(int fragmentId, String fragmentTag) {
mActiveFragmentIdStack.add(fragmentId);
mActiveFragmentTagStack.add(fragmentTag);
}
public void addToParentBackStack() {
if (mParentFragment != null) {
mParentFragment.addToBackStack(getId(), getTag());
} else if (mParentActivity != null) {
mParentActivity.addToBackStack(getId(), getTag());
}
}
}
Объяснение:
Каждое действие и фрагмент, расширенный из вышеперечисленных классов, управляет своим собственным задним стеком для каждого из своих детей, дочерних детей и так далее. Backstack - это просто запись тегов/идентификаторов "активного фрагмента". Поэтому предостережение заключается в том, чтобы всегда предоставлять тег и/или идентификатор для вашего фрагмента.
При добавлении в backstack в childFragmentManager вам также необходимо вызвать "addToParentBackStack()". Это гарантирует, что тег tag/id будет добавлен в родительский фрагмент/активность для последующих всплывающих окон.
Пример:
getChildFragmentManager().beginTransaction().replace(
R.id.fragment,
fragment,
fragment.getTag()
).addToBackStack(null).commit();
addToParentBackStack();
Ответ 12
Если у вас есть DialogFragment
, который, в свою очередь, имеет вложенные фрагменты, "обходной путь" немного отличается. Вместо того чтобы установить onKeyListener
в rootView
, вам нужно сделать это с помощью Dialog
. Также вы будете настраивать DialogInterface.OnKeyListener
, а не View
. Конечно, помните addToBackStack
!
Btw, имеющее 1 фрагмент в backsack для делегирования вызова назад к активности, является моим личным прецедентом. Типичные сценарии могут быть для count равными 0.
Вот что вы должны делать в onCreateDialog
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK){
FragmentManager cfm = getChildFragmentManager();
if(cfm.getBackStackEntryCount()>1){
cfm.popBackStack();
return true;
}
}
return false;
}
});
return dialog;
}
Ответ 13
для ChildFragments это работает.
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
} else {
doExit(); //super.onBackPressed();
}
}