在添加的最后一个未添加到返回堆栈的片段上按下后退按钮后,两个片段同时显示

时间:2020-12-29 14:20:28

标签: android android-fragments android-activity fragmenttransaction android-fragment-manager

例如,我有一个活动和三个片段(1、2、3)。

我显示第一个片段,然后是第二个片段(将其添加到后台堆栈)和第三个片段(不将其添加到后台堆栈,因为按下后退按钮时我想从当前的第三个片段转到第一个片段而不是继续到第二个片段)

这个逻辑有效,但是当它回到第一个片段时,第三个片段仍然可见(同时显示两个片段)

这是同时显示Fragment 1和Fragment 3时的完整流程示例和完整代码

enter image description here

enter image description here

enter image description here

enter image description here

活动:

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>

那么为什么第三个片段仍然可见?如何解决此问题以及处理此类交易的正确方法是什么?

如果他们的片段不透明,开发人员通常不会注意到这些问题,但在我的例子中它是透明的,所以白色背景 - 它是活动背景

0 个答案:

没有答案