Android 如何从FragmentManager中删除重复条目?

Android 如何从FragmentManager中删除重复条目?,android,android-fragments,kotlin,Android,Android Fragments,Kotlin,我有一个带有BottomNavigationView的简单活动。我使用片段来实现不同页面的活动内容 当用户按下后退按钮时,应该返回到之前查看的页面。问题是,当您在页面(片段)之间反复切换时,整个历史记录都会被记录下来。举个例子: A->B->A->B->C->A->C 按“后退”按钮会产生相反的结果,但我想要这种行为(我在Instagram应用程序中注意到): C->A->B->退出应用程序 因此,每个片段在backbackback中应该只有一个条目。我该怎么做?我是否从堆栈中删除片段的先前事务

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

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

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

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

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

因此,每个片段在backbackback中应该只有一个条目。我该怎么做?我是否从堆栈中删除片段的先前事务

使用FragmentManager是否可以实现这一点?还是我必须实施我自己的

使用BottomNavigationView的我的活动:

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()
        }
    }
}

此时,我非常确定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
        }
    }

}
类节点重复堆栈{
val堆栈:MutableList=mutableListOf()
val大小:Int
get()=stack.size
//将元素推到堆栈上
趣味推送(p:T){
val index=stack.indexOf(p)
如果(索引!=-1){
stack.removeAt(索引)
}
stack.add(p)
}
//弹出堆栈的上部元素
有趣的流行音乐:T{
如果(大小>0){
返回stack.removeAt(stack.size-1)
}否则{
返回空
}
}
//查看堆栈的上部元素,不要将其弹出
有趣的偷看:T{
如果(大小>0){
返回堆栈[stack.size-1]
}否则{
返回空
}
}
}
然后,我将该课程整合到我的活动中:

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)
        }
    }
}
类活动概述:AppCompatActivity(){
val fragmentsStack=NoDuplicateStack()
val fragmentHome=fragmentHome()
val fragmentSettings=fragmentSettings()
val fragmentHistory=fragmentHistory()
//BottomNavigationView的侦听器
private val mOnNavigationItemSelectedListener=。。。
重写创建时的乐趣(savedInstanceState:Bundle?){
...
}
重写函数onBackPressed(){
如果(碎片堆栈大小>1){
//从堆栈中删除当前片段
fragmentsStack.pop()
//从堆栈中获取上一个片段并再次设置它
val newTag=fragmentsStack.pop()
如果(newTag!=null){
setActiveFragment(新标记)
}
}否则{
完成()
}
}
//更新显示的片段
fun setActiveFragment(标记:String){
val fragment=when(标记){
resources.getString(R.string.tag\u fragment\u home)->fragmentHome
resources.getString(R.string.tag\u fragment\u settings)->fragmentSettings
resources.getString(R.string.tag\u fragment\u history)->fragmentHistory
else->null
}
if(fragment!=null&&!fragment.isVisible){
fragmentManager.beginTransaction()
.替换(R.id.container_碎片、碎片、标签)
.commit()
碎片堆叠推送(标签)
}
}
}

我也面临同样的问题,我使用了系统堆栈来解决这个问题

   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)
        }

直觉反应是:每次添加一个片段时,在片段管理器中查找它,如果它在那里,则将其删除(使用
FragmentTransaction.remove()
)。这样,您的片段管理器中只有一个给定片段的实例,最新的实例位于顶部。@BenP。但是我如何检索实际的事务,而不仅仅是片段?我有与您在问题中提到的相同的确切要求。我正在尝试实现您的解决方案,但是考虑到我使用的是java而不是kotlin,我无法实现与您使用的完全相同的逻辑。您是否也可以提供与java兼容的解决方案?