Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 4.2: back stack behaviour with nested fragments

With Android 4.2, the support library got support for nested fragments see here. I've played around with it and found an interesting behaviour / bug regarding back stack and getChildFragmentManager(). When using getChildFragmentManager() and addToBackStack(String name), by pressing the back button the system does not run down the back stack to the previous fragment. On the other hand, when using getFragmentManager() and addToBackStack(String name), by pressing the back button the system returns to the previous fragment.

For me, this behaviour is unexpected. By pressing the back button on my device, I'm expecting that the last added fragment to the back stack will be popped, even if the fragment was added to the back stack in the children's fragment manager.

Is this behaviour correct? Is this behaviour a bug? Is there a work around for this issue?

sample code with 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);     } }  } 

sample code with 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);     } }  } 
like image 963
AZ13 Avatar asked Nov 16 '12 14:11

AZ13


2 Answers

This solution may be better version of @Sean answer:

@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(); } 

Again, I prepared this solution based on @Sean answer above.

As @AZ13 said, this solution is only feasible in one level child fragments situations. In multiple level fragments case, works become a little complex, so I recommend that try this solution only the feasible case I have said. =)

Note: Since getFragments method is now a private method, this solution will not work. You can check comments for a link which suggests a solution about this situation.

like image 180
ismailarilik Avatar answered Sep 20 '22 12:09

ismailarilik


Seems like a bug. Take a look at: http://code.google.com/p/android/issues/detail?id=40323

For a workaround I've used successfully (as suggested in comments):

    @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();               } } 
like image 35
Sean Avatar answered Sep 18 '22 12:09

Sean