【问题标题】:How to remove duplicate entries from FragmentManager?如何从 FragmentManager 中删除重复条目?
【发布时间】:2017-07-21 14:29:54
【问题描述】:

我有一个BottomNavigationView 的简单活动。我正在使用片段来实现不同页面的活动内容。

当用户按下返回按钮时,它应该返回到之前查看的页面。问题是,当您在页面(片段)之间反复来回切换时,整个历史都会被记录下来。举个例子:

A -> B -> A -> B -> C -> A -> C

按下后退按钮会导致相反的结果,但我想要这种行为(我在 Instagram 应用程序中注意到它):

C -> A -> B -> 退出应用程序

所以每个片段在后台堆栈中应该只有一个条目。我该怎么做呢?我是否要从堆栈中删除片段的先前事务?

这完全可以使用 FragmentManager 吗?还是我必须自己实现?

我的带有底部导航视图的活动:

class ActivityOverview : AppCompatActivity() {

    // Listener for BottomNavigationView
    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->
        when (item.itemId) {
            R.id.navigation_home -> {
                // "Home" menu item pressed
                setActiveFragment(resources.getString(R.string.tag_fragment_home))
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_dashboard -> {
                // "Dashboard" menu item pressed
                return@OnNavigationItemSelectedListener true
            }
            R.id.navigation_settings -> {
                // "Settings" menu item pressed
                setActiveFragment(resources.getString(R.string.tag_fragment_settings))
                return@OnNavigationItemSelectedListener true
            }
        }
        false
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_overview)

        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
        navigation.menu.findItem(R.id.navigation_home).setChecked(true)
        // Set initial fragment
        setActiveFragment(resources.getString(R.string.tag_fragment_home))
    }

    override fun onBackPressed() {
        // > 1 so initial fragment addition isn't removed from stack
        if (fragmentManager.backStackEntryCount > 1) {
            fragmentManager.popBackStack()
        } else {
            finish()
        }
    }

    // Update displayed fragment
    fun setActiveFragment(tag: String) {
        val fragment = if (fragmentManager.findFragmentByTag(tag) != null) {
            // Fragment is already initialized
            if (fragmentManager.findFragmentByTag(tag).isVisible) {
                // Fragment is visible already, don't add another transaction
                null
            } else {
                // Fragment is not visible, add transaction
                fragmentManager.findFragmentByTag(tag)
            }
        } else {
            // Fragment is not initialized yet
            when (tag) {
                resources.getString(R.string.tag_fragment_home) -> FragmentHome()
                resources.getString(R.string.tag_fragment_settings) -> FragmentSettings()
                else -> null
            }
        }

        if (fragment != null) {
            val transaction = fragmentManager.beginTransaction()
            transaction.replace(R.id.container_fragment, fragment, tag)
            transaction.addToBackStack(null)
            transaction.commit()
        }
    }
}

【问题讨论】:

  • 直觉反应是:每次添加片段时,在片段管理器中查找它,如果存在则将其删除(使用FragmentTransaction.remove())。这样,您的片段管理器中只有一个给定片段的实例,最新的在顶部。
  • @BenP。但是如何检索实际事务,而不仅仅是片段?

标签: android android-fragments kotlin


【解决方案1】:

此时我很确定它不适用于 FragmentManager,因此我创建了一个类来实现不允许重复的堆栈:

class NoDuplicateStack<T> {

    val stack: MutableList<T> = mutableListOf()
    val size: Int
        get() = stack.size

    // Push element onto the stack
    fun push(p: T) {
        val index = stack.indexOf(p)
        if (index != -1) {
            stack.removeAt(index)
        }
        stack.add(p)
    }

    // Pop upper element of stack
    fun pop(): T? {
        if (size > 0) {
            return stack.removeAt(stack.size - 1)
        } else {
            return null
        }
    }

    // Look at upper element of stack, don't pop it
    fun peek(): T? {
        if (size > 0) {
            return stack[stack.size - 1]
        } else {
            return null
        }
    }

}

然后我将这个课程整合到我的活动中:

class ActivityOverview : AppCompatActivity() {

    val fragmentsStack = NoDuplicateStack<String>()
    val fragmentHome = FragmentHome()
    val fragmentSettings = FragmentSettings()
    val fragmentHistory = FragmentHistory()

    // Listener for BottomNavigationView
    private val mOnNavigationItemSelectedListener = ...

    override fun onCreate(savedInstanceState: Bundle?) {
        ...
    }

    override fun onBackPressed() {
        if (fragmentsStack.size > 1) {
            // Remove current fragment from stack
            fragmentsStack.pop()
            // Get previous fragment from stack and set it again
            val newTag = fragmentsStack.pop()
            if (newTag != null) {
                setActiveFragment(newTag)
            }
        } else {
            finish()
        }
    }

    // Update displayed fragment
    fun setActiveFragment(tag: String) {
        val fragment = when (tag) {
            resources.getString(R.string.tag_fragment_home) -> fragmentHome
            resources.getString(R.string.tag_fragment_settings) -> fragmentSettings
            resources.getString(R.string.tag_fragment_history) -> fragmentHistory
            else -> null
        }

        if (fragment != null && !fragment.isVisible) {
            fragmentManager.beginTransaction()
                    .replace(R.id.container_fragment, fragment, tag)
                    .commit()
            fragmentsStack.push(tag)
        }
    }
}

【讨论】:

  • 我的要求与您在问题中提到的完全相同。我正在尝试实现您的解决方案,但是鉴于我使用的是 java 而不是 kotlin,因此我无法实现您使用的完全相同的逻辑。您是否也可以提供与 java 兼容的解决方案?
【解决方案2】:

我也遇到了同样的问题,我这样做是使用系统堆栈的

   val totalFragments = supportFragmentManager.backStackEntryCount

            if (totalFragments != 0) {
                val removed = supportFragmentManager.getBackStackEntryAt(totalFragments - 1)
                poppedFragments.add(removed.name!!)

                for (idx in totalFragments - 1  downTo 0) {
                    val fragment = supportFragmentManager.getBackStackEntryAt(idx)
                    if (!poppedFragments.contains(fragment.name)) {
                        supportFragmentManager.popBackStack(fragment.name, 0)
                        return
                    }
                }

                finish()
                return
            }

            super.onBackPressed()

然后在启动片段时添加它

    if (poppedFragments.contains(tag)) {
            poppedFragments.remove(tag)
        }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-17
    • 2017-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    • 2013-11-08
    相关资源
    最近更新 更多