Android 4.2: back stack behaviour with nested fragments

后端 未结 17 1725
爱一瞬间的悲伤
爱一瞬间的悲伤 2020-11-29 16:01

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

相关标签:
17条回答
  • 2020-11-29 16:43

    After observing some solutions presented here, I found that to allow flexibility & control for the parent fragment as to when popping the stack or when the back action should be ignored by it, I rather use such implementation:

    Defining a "ParentFragment" interface:

    interface ParentFragment {
    /**
     * Fragments that host child fragments and want to control their BackStack behaviour when the back button is pressed should implement this
     *
     * @return true if back press was handled, false otherwise
     */
    fun onBackPressed(): Boolean
    

    }

    Overriding the "onBackPressed" in the parent activity (or in the BaseActivity):

    override fun onBackPressed() {
        val fm: FragmentManager = supportFragmentManager
        for (frag in fm.fragments) {
            if (frag.isVisible && frag is ParentFragment && frag.onBackPressed()) {
                return
            }
        }
        super.onBackPressed()
    }
    

    And then allow the parent fragment to handle as it wishes, for example:

    override fun onBackPressed(): Boolean {
        val childFragment = childFragmentManager.findFragmentByTag(SomeChildFragment::class.java.simpleName)
        if (childFragment != null && childFragment.isVisible) {
            // Only for that case, pop the BackStack (perhaps when other child fragments are visible don't)
            childFragmentManager.popBackStack()
            return true
        }
        return false
    }
    

    This allows to avoid thinking that there is some legitimate child fragment to remove when using a view pager for example (and back stack entry count > 0).

    0 讨论(0)
  • 2020-11-29 16:45

    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.

    0 讨论(0)
  • 2020-11-29 16:45

    This code will navigate the tree of fragment managers and return the last one that was added that has any fragments it can pop off the stack:

    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;
    }
    

    Call the method:

    @Override
    public void onBackPressed()
    {
      FragmentManager fm = getLastFragmentManagerWithBack(getSupportFragmentManager());
    
      if (fm.getBackStackEntryCount() > 0)
      {
        fm.popBackStack();
        return;
      }
    
      super.onBackPressed();
    }
    
    0 讨论(0)
  • 2020-11-29 16:51

    With this answer it will handle recursive back checking and give each fragment the chance to override the default behaviour. This means you can have a fragment that hosts a ViewPager do something special like scroll to the page that as a back-stack, or scroll to the home page and then on the next back press exit.

    Add this to your Activity that extends AppCompatActivity.

    @Override
    public void onBackPressed()
    {
        if(!BaseFragment.handleBackPressed(getSupportFragmentManager())){
            super.onBackPressed();
        }
    }
    

    Add this to your BaseFragment or the class you can have all your fragments inherit from.

    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;
    }
    
    0 讨论(0)
  • 2020-11-29 16:54

    The reason is that your Activity derives from FragmentActivity, which handles the BACK key press (see line 173 of FragmentActivity.

    In our application, I'm using a ViewPager (with fragments) and each fragment can have nested fragments. The way I've handled this is by:

    • defining an interface OnBackKeyPressedListener with a single method void onBackKeyPressed()
    • implemented this interface in "top" fragments that ViewPager shows
    • overriding onKeyDown and detecting BACK press, and calling onBackKeyPressed in a currently active fragment in the view pager.

    Also note, that I'm using getChildFragmentManager() in the fragments to properly nest fragments. You can see a discussion and an explanation in this android-developers post.

    0 讨论(0)
提交回复
热议问题