How to resume Fragment from BackStack if exists

前端 未结 6 995
野的像风
野的像风 2020-11-22 13:29

I am learning how to use fragments. I have three instances of Fragment that are initialized at the top of the class. I am adding the fragment to an activity lik

相关标签:
6条回答
  • 2020-11-22 14:09

    I think this method my solve your problem:

    public static void attachFragment ( int fragmentHolderLayoutId, Fragment fragment, Context context, String tag ) {
    
    
        FragmentManager manager = ( (AppCompatActivity) context ).getSupportFragmentManager ();
        FragmentTransaction ft = manager.beginTransaction ();
    
        if (manager.findFragmentByTag ( tag ) == null) { // No fragment in backStack with same tag..
            ft.add ( fragmentHolderLayoutId, fragment, tag );
            ft.addToBackStack ( tag );
            ft.commit ();
        }
        else {
            ft.show ( manager.findFragmentByTag ( tag ) ).commit ();
        }
    }
    

    which was originally posted in This Question

    0 讨论(0)
  • 2020-11-22 14:11
    getFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
    
        @Override
        public void onBackStackChanged() {
    
            if(getFragmentManager().getBackStackEntryCount()==0) {
                onResume();
            }    
        }
    });
    
    0 讨论(0)
  • 2020-11-22 14:12

    Step 1: Implement an interface with your activity class

    public class AuthenticatedMainActivity extends Activity implements FragmentManager.OnBackStackChangedListener{
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            .............
            FragmentManager fragmentManager = getFragmentManager();           
            fragmentManager.beginTransaction().add(R.id.frame_container,fragment, "First").addToBackStack(null).commit();
        }
    
        private void switchFragment(Fragment fragment){            
          FragmentManager fragmentManager = getFragmentManager();
          fragmentManager.beginTransaction()
            .replace(R.id.frame_container, fragment).addToBackStack("Tag").commit();
        }
    
        @Override
        public void onBackStackChanged() {
        FragmentManager fragmentManager = getFragmentManager();
    
        System.out.println("@Class: SummaryUser : onBackStackChanged " 
                + fragmentManager.getBackStackEntryCount());
    
        int count = fragmentManager.getBackStackEntryCount();
    
        // when a fragment come from another the status will be zero
        if(count == 0){
    
            System.out.println("again loading user data");
    
            // reload the page if user saved the profile data
    
            if(!objPublicDelegate.checkNetworkStatus()){
    
                objPublicDelegate.showAlertDialog("Warning"
                        , "Please check your internet connection");
    
            }else {
    
                objLoadingDialog.show("Refreshing data..."); 
    
                mNetworkMaster.runUserSummaryAsync();
            }
    
            // IMPORTANT: remove the current fragment from stack to avoid new instance
            fragmentManager.removeOnBackStackChangedListener(this);
    
        }// end if
       }       
    }
    

    Step 2: When you call the another fragment add this method:

    String backStateName = this.getClass().getName();
    
    FragmentManager fragmentManager = getFragmentManager();
    fragmentManager.addOnBackStackChangedListener(this); 
    
    Fragment fragmentGraph = new GraphFragment();
    Bundle bundle = new Bundle();
    bundle.putString("graphTag",  view.getTag().toString());
    fragmentGraph.setArguments(bundle);
    
    fragmentManager.beginTransaction()
    .replace(R.id.content_frame, fragmentGraph)
    .addToBackStack(backStateName)
    .commit();
    
    0 讨论(0)
  • 2020-11-22 14:24

    I know this is quite late to answer this question but I resolved this problem by myself and thought worth sharing it with everyone.`

    public void replaceFragment(BaseFragment fragment) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        final FragmentManager fManager = getSupportFragmentManager();
        BaseFragment fragm = (BaseFragment) fManager.findFragmentByTag(fragment.getFragmentTag());
        transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right);
    
        if (fragm == null) {  //here fragment is not available in the stack
            transaction.replace(R.id.container, fragment, fragment.getFragmentTag());
            transaction.addToBackStack(fragment.getFragmentTag());
        } else { 
            //fragment was found in the stack , now we can reuse the fragment
            // please do not add in back stack else it will add transaction in back stack
            transaction.replace(R.id.container, fragm, fragm.getFragmentTag()); 
        }
        transaction.commit();
    }
    

    And in the onBackPressed()

     @Override
    public void onBackPressed() {
        if(getSupportFragmentManager().getBackStackEntryCount()>1){
            super.onBackPressed();
        }else{
            finish();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 14:30

    Reading the documentation, there is a way to pop the back stack based on either the transaction name or the id provided by commit. Using the name may be easier since it shouldn't require keeping track of a number that may change and reinforces the "unique back stack entry" logic.

    Since you want only one back stack entry per Fragment, make the back state name the Fragment's class name (via getClass().getName()). Then when replacing a Fragment, use the popBackStackImmediate() method. If it returns true, it means there is an instance of the Fragment in the back stack. If not, actually execute the Fragment replacement logic.

    private void replaceFragment (Fragment fragment){
      String backStateName = fragment.getClass().getName();
    
      FragmentManager manager = getSupportFragmentManager();
      boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
    
      if (!fragmentPopped){ //fragment not in back stack, create it.
        FragmentTransaction ft = manager.beginTransaction();
        ft.replace(R.id.content_frame, fragment);
        ft.addToBackStack(backStateName);
        ft.commit();
      }
    }
    

    EDIT

    The problem is - when i launch A and then B, then press back button, B is removed and A is resumed. and pressing again back button should exit the app. But it is showing a blank window and need another press to close it.

    This is because the FragmentTransaction is being added to the back stack to ensure that we can pop the fragments on top later. A quick fix for this is overriding onBackPressed() and finishing the Activity if the back stack contains only 1 Fragment

    @Override
    public void onBackPressed(){
      if (getSupportFragmentManager().getBackStackEntryCount() == 1){
        finish();
      }
      else {
        super.onBackPressed();
      }
    }
    

    Regarding the duplicate back stack entries, your conditional statement that replaces the fragment if it hasn't been popped is clearly different than what my original code snippet's. What you are doing is adding to the back stack regardless of whether or not the back stack was popped.

    Something like this should be closer to what you want:

    private void replaceFragment (Fragment fragment){
      String backStateName =  fragment.getClass().getName();
      String fragmentTag = backStateName;
    
      FragmentManager manager = getSupportFragmentManager();
      boolean fragmentPopped = manager.popBackStackImmediate (backStateName, 0);
    
      if (!fragmentPopped && manager.findFragmentByTag(fragmentTag) == null){ //fragment not in back stack, create it.
        FragmentTransaction ft = manager.beginTransaction();
        ft.replace(R.id.content_frame, fragment, fragmentTag);
        ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
        ft.addToBackStack(backStateName);
        ft.commit();
      } 
    }
    

    The conditional was changed a bit since selecting the same fragment while it was visible also caused duplicate entries.

    Implementation:

    I highly suggest not taking the the updated replaceFragment() method apart like you did in your code. All the logic is contained in this method and moving parts around may cause problems.

    This means you should copy the updated replaceFragment() method into your class then change

    backStateName = fragmentName.getClass().getName();
    fragmentPopped = manager.popBackStackImmediate(backStateName, 0);
    if (!fragmentPopped) {
                ft.replace(R.id.content_frame, fragmentName);
    }
    ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
    ft.addToBackStack(backStateName);
    ft.commit();
    

    so it is simply

    replaceFragment (fragmentName);
    

    EDIT #2

    To update the drawer when the back stack changes, make a method that accepts in a Fragment and compares the class names. If anything matches, change the title and selection. Also add an OnBackStackChangedListener and have it call your update method if there is a valid Fragment.

    For example, in the Activity's onCreate(), add

    getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener() {
    
      @Override
      public void onBackStackChanged() {
        Fragment f = getSupportFragmentManager().findFragmentById(R.id.content_frame);
        if (f != null){
          updateTitleAndDrawer (f);
        }
    
      }
    });
    

    And the other method:

    private void updateTitleAndDrawer (Fragment fragment){
      String fragClassName = fragment.getClass().getName();
    
      if (fragClassName.equals(A.class.getName())){
        setTitle ("A");
        //set selected item position, etc
      }
      else if (fragClassName.equals(B.class.getName())){
        setTitle ("B");
        //set selected item position, etc
      }
      else if (fragClassName.equals(C.class.getName())){
        setTitle ("C");
        //set selected item position, etc
      }
    }
    

    Now, whenever the back stack changes, the title and checked position will reflect the visible Fragment.

    0 讨论(0)
  • 2020-11-22 14:32

    Easier solution will be changing this line

    ft.replace(R.id.content_frame, A); to ft.add(R.id.content_frame, A);

    And inside your XML layout please use

      android:background="@color/white"
      android:clickable="true"
      android:focusable="true"
    

    Clickable means that it can be clicked by a pointer device or be tapped by a touch device.

    Focusable means that it can gain the focus from an input device like a keyboard. Input devices like keyboards cannot decide which view to send its input events to based on the inputs itself, so they send them to the view that has focus.

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