0

Suppose I'm in fragment A, then moving to B, then using Back button returns to A. In the activity I'm performing the following override:

@Override
public void onBackPressed(){
    FragmentManager fm = getSupportFragmentManager();
    Fragment frag = fm.findFragmentByTag(Consts.A);
    if (frag != null){
        Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
        fm.beginTransaction().remove(frag).commit();
        fm.popBackStack();
    }
}

and while showing B goes like this:

                FragmentManager fm = getActivity().getSupportFragmentManager();
            Fragment f = BFragment.newInstance(Consts.B);
            fm.beginTransaction()
                    .replace(R.id.rl_content,
                            f,
                            Consts.B)
                    .addToBackStack(null)  
                    .commit();

Now, which method (if any) will be executed in A, once we execute popBackStack()? If none, how can we change A's data models or UI components (such as keyboard or a TextView) right after back press? is it component-dependent?

R.id.rl_content is the container. Please consider 2 cases: 1. A is in R.id.rl and being replaced 2. A is not in R.id.rl and is not being replaced

gpgekko
  • 3,506
  • 3
  • 32
  • 35
lionheart
  • 437
  • 11
  • 33

2 Answers2

2

If you're always going back from Fragment B to Fragment A or vice versa, i would recommend this solution inside the fragments themselves.

@Override
    public void onResume() {
        super.onResume();

        Fragment f = AFragment.newInstance(Consts.A);

        if(getView() == null){
            return;
        }
        getView().setFocusableInTouchMode(true);
        getView().requestFocus();
        getView().setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
                    FragmentTransaction trans = getFragmentManager().beginTransaction();
                    trans.replace(R.id.rl_content, f);
                    trans.addToBackStack(null);
                    trans.commit();
                    return true;
                }
                return false;
            }
        });
    }

You can freely move from B to A and A to B using the same code. if you would like a more dynamic approach e.g. you would like to go from Fragment A to Fragment C, or Fragment B to Fragment C and then when you press back go back to the previous fragment on stack. I would aim to use Kyle Falconer's Solution here

Incase the link dies, I'll post the code here:

@Override
public void onBackPressed(){
    FragmentManager fm = getFragmentManager();
    if (fm.getBackStackEntryCount() > 0) {
        Log.i("MainActivity", "popping backstack");
        fm.popBackStack();
    } else {
        Log.i("MainActivity", "nothing on backstack, calling super");
        super.onBackPressed();  
    }
}

I haven't tested the second solution, but use the first.

Community
  • 1
  • 1
BradleyIW
  • 1,338
  • 2
  • 20
  • 37
  • 1
    +1 since on most occasions these solutions will probably work (considering f would be final) on most occasions, but in some (showing/hiding soft keyboard, permission denial, etc.) these wouldn't work and we're back to square one. – lionheart Dec 07 '16 at 15:21
  • very valid point, I would assume you'd approach this with the same logic to allow yourself to overcome these issues e.g. onfocuschange listener etc etc – BradleyIW Dec 07 '16 at 15:36
  • I'm accepting this as answer, since I did use the link and especially the (fm.getBackStackEntryCount() > 0) condition in various situations. – lionheart Jan 23 '17 at 09:41
1

There are quiet a few ways by which you can change A's data models or UI components.

Case 1: when A is in R.id.rl_content and is being replaced by B. In this case you can simply update required models or UI in onCreateView of Fragment A.

Case 2: When A is not being replaced. In this case fragment A doesn't know when to update its view. In the onBackpressed() of your activity you can call Fragment A's updateView() method if Fragment B is being popped.

@Override 
public void onBackPressed(){ 
    FragmentManager fm = getSupportFragmentManager(); 
    FragmentB fragmentB = (FragmentB)fm.findFragmentByTag(Consts.B); 
    if (fragmentB != null){ 
        Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
        fm.beginTransaction().remove(fragmentB).commit(); 
        fm.popBackStack(); 
        FragmentA fragmentA = (FragmentA)fm.findFragmentByTag(Consts.A);
        if (fragmentA != null) {
             fragmentA.updateView();
        }
    } 
} 

EDIT

I understand that you also want to handle scenarios like hiding keyboard etc. For this you might want to pass backpress event to the individual fragments. Somewhat like this:

@Override 
public void onBackPressed(){ 
    FragmentManager fm = getSupportFragmentManager(); 
    FragmentB fragmentB = (FragmentB)fm.findFragmentByTag(Consts.B); 
    if (fragmentB != null){ 
            if (!fragmentB.onBackPress()) {
                 // This means fragment B doesn't want to consume backpress therefore remove it.
                 Log.d(Consts.TAGS.ACTIVITY_ORDER,"");
                 fm.beginTransaction().remove(fragmentB).commit(); 
                 fm.popBackStack(); 
                 FragmentA fragmentA = (FragmentA)fm.findFragmentByTag(Consts.A);
                 if (fragmentA != null) {
                     fragmentA.updateView();
                 }
            }
        } 
    } 

And in your Fragment B create a function onBackPress like this:

public boolean onBackPressed() {
  // if keyboard is showing then hide it here and return true to consume the back press event or else return false to dismiss this fragment. 
}
Varun Verma
  • 692
  • 7
  • 13
  • +1 since in Case 2 - I do work with interfaces and this solution will probably work on most occasions, and Case 1 is true. But, there are some cases (for example, soft keyboard, permission denail handling, etc.) where this solution (on most devices) fails to work. I never wrote it, but I did mention that I've tried using onBackPressed. – lionheart Dec 07 '16 at 15:17
  • 1
    Okay So I understand that you want to handle cases which are dependent to individual fragments. In that case you should ask from individual fragments whether they want to handle backpress event. I have explained this in edited version of my answer. – Varun Verma Dec 07 '16 at 15:53
  • It doesn't work and there might be a 'hackish' way of doing it. It seems there are things that go under the hood that make some other things don't work. It might be specific to this case. I'm not sure. – lionheart Dec 08 '16 at 13:51