1

Short Version:

I have 5 fragments all added to a container by replace method(A -> B -> C -> D -> E), now I want to remove 3rdfragment from my backStack(C) and stay in the current page, how to do it?

The problem:

I have 3 fragments, FragmentA, FragmentB, and FragmentC, and user navigates from A to B then C. In FragmentC when user performs an action (I call it ActionX) I need to remove FragmentB from my fragment manager BackStack (while user still is in FragmentC) and I don't want user to notice anything, just when he presses back on phone different fragment shows (so if ActionX happened when user pressed back fragmentA shows, if not FragmentB shows)

This is how I show my fragments (I set fragment name for name and tag to find them when I need)

public void showFragment(Fragment fragment) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        String tag = fragment.getClass().getSimpleName();
        fragmentTransaction.replace(R.id.fragment_container, fragment, tag);
        fragmentTransaction.addToBackStack(tag);
        fragmentTransaction.commit();
}

I tried solutions mentiond in some SO threads (like this) but they only work on top fragment in stack or they suggest to not FragmentB (which I cant because I don't know what will user do), also finding and removing FragmentB from stack does not work. One solution is to override my Activity's onBackPressed and check if ActionX happend and pop my backStack twice.

what I tried:

  1. this don't work (fragmentBInStack is not null but when I remove it nothing happens)
val fragmentBInStack = manager.findFragmentByTag(FragmentB::class.java.simpleName)
fragmentBInStack?.let {
    manager.beginTransaction().remove(it).commit()
}
  1. this code removes both FragmentC and FragmentB and shows FragmentA (I want to stay in FragmentC) if I user 0 instad of POP_BACK_STACK_INCLUSIVE it only pops FragmentC and shows FragmentB
manager.popBackStack(FragmentB::class.java.simpleName, FragmentManager.POP_BACK_STACK_INCLUSIVE)

this is some sample code of what I want to do

class FragmentC : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<View>(R.id.action_x).setOnClickListener {

            //here I want to remove FragmentB from back Stack and stay in FragmentC, just later when user presses back I want to show FragmentA
            val manager = activity!!.supportFragmentManager
            val fragmentBInStack = manager.findFragmentByTag(FragmentB::class.java.simpleName)
            fragmentBInStack?.let {
                manager.beginTransaction().remove(it).commit()
            }
        }
    }

feridok
  • 648
  • 7
  • 26

2 Answers2

0

Ok, first of all, you have to add your fragments in seperate transaction, and change replace to add

supportFragmentManager
    .beginTransaction()
    .add(R.id.fragment_container, fragment2, tag2)
    .addToBackStack(tag2)
    .commit()

Then you remove fragment as you do:

val fragment2 = supportFragmentManager.findFragmentByTag(tag2)!!
supportFragmentManager.beginTransaction().remove(fragment2).commit()

The last step is just to popBackStack:

supportFragmentManager.popBackStack()
mac229
  • 4,319
  • 5
  • 18
  • 24
  • thanks for your answer, but I always have problems with add when I use it so I prefer using replace, your code works well if I use add(). do you have any suggestions for removing fragment when I used replace()? – feridok Jan 18 '20 at 12:17
  • also when I use this code (using add) when I go back to FragmentA, I have to press back button twice to go back to the previous fragment (try it) – feridok Jan 18 '20 at 12:21
  • you can avoid pressing back button twice adding the first fragment without adding it to back stack – mac229 Jan 18 '20 at 12:24
  • I understand, but replace method is essentially the same as calling remove(Fragment) for all currently added fragments. That's why it doesnt work – mac229 Jan 18 '20 at 12:40
  • do you really yourself use add() in large applications? – feridok Jan 18 '20 at 13:18
  • Yes, I don't have problems with add() – mac229 Jan 18 '20 at 13:22
0

I can suggest the following approach: use the same instance of ViewModel (use ViewModelProvider of activity to create view model instance) with ActionX event LiveData in FragmentB and FragmentC. In FragmentC the value of LiveData is set, while FragmentB observes live data changes.

Here is the source code.

1. view model

class InterFragmentViewModel : ViewModel() {
    val actionXLiveData = MutableLiveData<Boolean>()
}

2. FragmentC

class FragmentC : AppCompatDialogFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        view.findViewById<View>(R.id.action_x).setOnClickListener {
            ViewModelProvider(requireActivity()).get(InterFragmentViewModel::class.java)
                .actionXLiveData.value = true
        }
    }
}

3. FragmentB

class FragmentB : AppCompatDialogFragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ViewModelProvider(requireActivity()).get(InterFragmentViewModel::class.java)
            .actionXLiveData.observe(viewLifecycleOwner, Observer {
            parentFragmentManager.popBackStack()
        })

    }
}
art
  • 1,222
  • 9
  • 19