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 an
I have implemented it correctly if anybody didnt found any answer till now
just add this method on your child nested fragment
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
// This callback will only be called when MyFragment is at least Started.
OnBackPressedCallback callback = new OnBackPressedCallback(true ) {
@Override
public void handleOnBackPressed() {
// Handle the back button event
FragmentManager fm= getFragmentManager();
if (fm != null) {
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
Log.e( "Frag","back" );
}
List<Fragment> fragList = fm.getFragments();
if (fragList != null && fragList.size() > 0) {
for (Fragment frag : fragList) {
if (frag == null) {
continue;
}
if (frag.isVisible()) {
Log.e( "Frag","Visible" );
}
}
}
}
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
super.onCreate( savedInstanceState );
}
i was able to handle fragment back stack by adding to the parent fragment this method at the onCreate View() method and passing the root 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;
}
} );
}
The method isEnableFragmentBackStack() is a boolean flag to know when i'm on the main fragment or the next one.
Make sure that when you commit the fragment that needs to be have stack, then you must add the addToBackstack Method.
Thanks to everyone for their help, this (tweaked version) works for me:
@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;
}
NOTE: You will probably also want to set the background color of these nested fragments to the app theme's window background color, as by default they are transparent. Somewhat outside of the scope of this question, but it is accomplished by resolving the attribute android.R.attr.windowBackground, and setting the Fragment view's background to that resource ID.
More than 5 years and this issue is still relevant. If you do not want to use the fragmentManager.getFragments() due to its restriction. Extend and use the below classes:
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());
}
}
}
Explanation:
Each activity and fragment extended from the above classes manages their own back stack for each of their children, and children's children, and so on. The backstack is simply a record of "active fragment" tags/ids. So the caveat is to always provide a tag and/or id for your fragment.
When adding to the backstack in a childFragmentManager, you will need to also call "addToParentBackStack()". This ensures that the fragment's tag/id is added in the parent fragment/activity for later pops.
Example:
getChildFragmentManager().beginTransaction().replace(
R.id.fragment,
fragment,
fragment.getTag()
).addToBackStack(null).commit();
addToParentBackStack();
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();
}
}
if you are using fragment in a fragment then use getChildFragmentManager()
getChildFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();
if you are using child fragment and replace it use getParentFragmentManager()
getParentFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();
if both are not working for you try this getActivity().getSupportFragmentManager()
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.fragment_name, fragment).addToBackStack(null).commit();