1

For example I have one activity and three fragments (1, 2, 3).

I show first fragment, then second fragment (adding it to back stack) and third fragment (no adding it to back stack because when pressing the back button I want to go to the first fragment from the current third one instead of going to the second fragment)

This logic works but when it go back to first fragment, third fragment is still visible (two fragments are displayed at the same time)

Here's the full flow example and the full code when Fragment 1 and Fragment 3 are displayed at the same time

enter image description here

enter image description here

enter image description here

enter image description here

Activity:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        showFragment(
            addToBackStack = false,
            backStackName = null,
            fragmentNumber = 1,
            tag = "FRAGMENT_FIRST"
        )

        // simulate going to other fragments sequentially
        val mainHandler = Handler(Looper.getMainLooper())

        mainHandler.postDelayed(
            {
                showFragment(
                    addToBackStack = true,
                    backStackName = null,
                    fragmentNumber = 2,
                    tag = "FRAGMENT_SECOND"
                )
            },
            1000L
        )

        mainHandler.postDelayed(
            {
                showFragment(
                    addToBackStack = false, // when back pressing from third fragment I want it to get back to first fragment, not second
                    backStackName = null,
                    fragmentNumber = 3,
                    tag = "FRAGMENT_THIRD"
                )
            },
            2000L
        )
    }

    private fun showFragment(
        addToBackStack: Boolean,
        backStackName: String?,
        fragmentNumber: Int,
        tag: String
    ) {
        val transaction = supportFragmentManager.beginTransaction()

        var fragment =
            supportFragmentManager.findFragmentByTag(tag) as FragmentGeneral?
        if (fragment == null) {
            fragment = FragmentGeneral.newInstance(number = fragmentNumber)
            transaction.replace(R.id.fragment_container, fragment, tag)
            if (addToBackStack) {
                transaction.addToBackStack(backStackName)
            }
        } else {
            transaction.show(fragment)
        }

        transaction.commit()
    }
}

class FragmentGeneral : Fragment() {

    private lateinit var binding: FragmentGeneralBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ) = FragmentGeneralBinding.inflate(layoutInflater, container, false).apply {
        binding = this
    }.root

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        arguments?.getInt(ARG_FRAGMENT_NUMBER)?.let { number ->
            binding.fragmentNumber = number

            // set different position of the text so we could clearly see overlapping of two fragments
            binding.textView.gravity = when (number) {
                1 -> Gravity.TOP
                2 -> Gravity.CENTER_VERTICAL
                3 -> Gravity.BOTTOM
                else -> return
            }

            // simulate pressing back button pressing
            if (number == 3) {
                // should go to first fragment because it wasn't added to backstack
                Handler(Looper.getMainLooper()).postDelayed(
                    { activity?.onBackPressed() },
                    1000L
                )
            }
        }
    }

    companion object {
        private const val ARG_FRAGMENT_NUMBER = "ARG_FRAGMENT_NUMBER"

        fun newInstance(number: Int) =
            FragmentGeneral().apply {
                arguments = bundleOf(ARG_FRAGMENT_NUMBER to number)
            }
    }
}

activity_main.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".MainActivity">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <FrameLayout
            android:id="@+id/fragment_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

    </FrameLayout>
</layout>

fragment_general.xml:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".FragmentGeneral">

    <data>
        <variable
            name="fragmentNumber"
            type="int" />
    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/text_view"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:text='@{"Fragment #" + fragmentNumber}'
            android:textColor="@android:color/black"
            android:textSize="64sp"
            tools:text="Fragment #?" />
    </FrameLayout>
</layout>

So why third fragment is still visible? How to fix this issue and what is correct way to handle such transactions?

Developers usually would not notice such issues if their fragments are not transparent, but in my example it's transparent, so the white background - it's activity background

user924
  • 8,146
  • 7
  • 57
  • 139

0 Answers0