kotlin 中的 backstack 和 bottomnav 问题
Issue with backstack and bottomnav in kotlin
我有一个包含 4 个片段的底部导航,主页、关注、通知和配置文件,backstack 上的底部导航没有问题,但是现在例如从配置文件片段我跳转到一个名为 edit_profile 这不是底部导航的一部分,当按回时,我希望它应该返回到配置文件片段,但 backstack 将我从 edit_profile 直接带到主页片段
这里是录音link
我最近将我的项目从 java 更改为 kotlin,我是 kotlin 的初学者
我非常喜欢 Pinterest 和 Instagram 的导航
Note:- All this code is automatically changed to kotlin (with some
changes done manually ) , this issue was also with java and not after migrating to kotlin , Also if you want more reference of the code
please tell me i will update the question
代码
MainActivity.kt // 底部导航
class MainActivity : AppCompatActivity() {
var bottomNavigationView: BottomNavigationView? = null
var integerDeque: Deque<Int> = ArrayDeque(3)
var flag = true
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
setContentView(R.layout.activity_main)
val window = this.window
window.statusBarColor = this.resources.getColor(R.color.black)
bottomNavigationView = findViewById(R.id.bottom_navigation_view)
integerDeque.push(R.id.nav_home)
loadFragments(Home_Fragment())
bottomNavigationView!!.selectedItemId = R.id.nav_home
bottomNavigationView!!.setOnNavigationItemSelectedListener(
BottomNavigationView.OnNavigationItemSelectedListener { item: MenuItem ->
val id = item.itemId
if (integerDeque.contains(id)) {
if (id == R.id.nav_home) {
integerDeque.size
if (flag) {
integerDeque.addFirst(R.id.nav_home)
flag = false
}
}
integerDeque.remove(id)
}
integerDeque.push(id)
loadFragments(getFragment(item.itemId))
false
}
)
}
@SuppressLint("NonConstantResourceId")
private fun getFragment(itemId: Int): Fragment {
when (itemId) {
R.id.nav_home -> {
bottomNavigationView!!.menu.getItem(0).isChecked = true
return Home_Fragment()
}
R.id.nav_following -> {
bottomNavigationView!!.menu.getItem(1).isChecked = true
return Following_Fragment()
}
R.id.nav_notification -> {
bottomNavigationView!!.menu.getItem(2).isChecked = true
return Notification_Fragment()
}
R.id.nav_profile -> {
bottomNavigationView!!.menu.getItem(3).isChecked = true
return Profile_Fragment()
}
}
bottomNavigationView!!.menu.getItem(0).isChecked = true
return Home_Fragment()
}
private fun loadFragments(fragment: Fragment?) {
if (fragment != null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment, fragment.javaClass.simpleName)
.commit()
}
}
override fun onBackPressed() {
integerDeque.pop()
if (!integerDeque.isEmpty()) {
loadFragments(getFragment(integerDeque.peek()))
} else {
finish()
}
}
Edit_Profile.kt // 从这个片段我想回到最后一个应该是个人资料片段的片段
class Edit_Profile : Fragment() {
private var profilePhoto: CircleImageView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_edit_profile, container, false)
profilePhoto = view.findViewById(R.id.circleImageView)
initImageLoader()
setProfileImage()
val imageView = view.findViewById<ImageView>(R.id.backArrow)
imageView.setOnClickListener {
val newCase: Fragment = Profile_Fragment()
assert(fragmentManager != null)
val transaction = requireFragmentManager().beginTransaction()
transaction.replace(R.id.fragment_container, newCase)
transaction.addToBackStack(Profile_Fragment.toString())
transaction.commit()
}
return view
}
编辑
添加了从个人资料片段到编辑个人资料的交易的一部分
ProfileFragment.kt
editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
val edit_profile: Fragment = Edit_Profile()
requireActivity().getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, edit_profile,"TAG")
.addToBackStack("TAG")
.commit()
})
通常我遵循这个模式
我在包含所有底部导航选项卡的主容器中添加 HomeF,所有底部导航选项卡都将在主容器中打开,而那些不属于底部导航的片段将在主容器中打开。我通常添加(而不是替换)主容器中的所有片段并设置添加到后台堆栈,这样如果用户从配置文件(home_container)转到主容器中的某些内容,而后台我们可以弹出顶部片段和用户将看到个人资料。
现在您正在通过 integerDeque
数组管理返回堆栈。
- 当您转到新的
BottomNavigationView
片段时;如果它不存在,你将它的 id 添加到数组中。
- 弹出返回栈时;顶部的片段被踢出数组。
但是由于您在 bottomNavigationView.setOnItemSelectedListener
回调中推送了所有这些 ID;那么 integerDeque
数组只包含 BottomNavigationView
个片段 ID。
并且由于 Edit_Profile
片段不是 BottomNavigationView
片段的一部分,因此它不会 added/popped 离开队列。相反,当您尝试在显示 Edit_Profile
片段时弹出返回堆栈时;您在 onBackPressed()
中管理的正常行为将继续,并且 Profile_Fragment
id 将从队列中弹出,使您 return 到您提到的示例中的前面片段 (Home_Fragment
)。
解决这个问题的一个小办法是考虑在处理 Edit_Profile
片段时将一个 id 添加到队列中,这样这个 id 就会从队列中弹出,从而返回到 Profile_Fragment
片段。
您可以使用片段的 ID 执行此操作以确保它是唯一的:
editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
val edit_profile: Fragment = Edit_Profile()
requireActivity().getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, edit_profile,"TAG")
.addToBackStack("TAG")
.commit()
(requireActivity() as MainActivity).integerDeque.push(id) // <<<< pushing id to the queue
})
这应该可以解决您的问题。
小贴士:
- 在
BNV
上使用 setOnItemSelectedListener
而不是 setOnNavigationItemSelectedListener
,因为后者已被弃用。
- Return
true
而不是 setOnItemSelectedListener
回调中的 false
因为这应该消耗事件并将 BNV
标记为已选择。
- 在
Edit_Profile
事务中 replace
片段而不是用 add
添加片段,因为容器已经被消耗;这将使您避免容器中的重叠片段。
- 在
onBackPressed()
;您将 loadFragments(..)
替换为 bottomNavigationView.selectedItemId = integerDeque.peek()
;重用相同的片段而不是重做事务可能更轻松。
我有一个包含 4 个片段的底部导航,主页、关注、通知和配置文件,backstack 上的底部导航没有问题,但是现在例如从配置文件片段我跳转到一个名为 edit_profile 这不是底部导航的一部分,当按回时,我希望它应该返回到配置文件片段,但 backstack 将我从 edit_profile 直接带到主页片段
这里是录音link
我最近将我的项目从 java 更改为 kotlin,我是 kotlin 的初学者
我非常喜欢 Pinterest 和 Instagram 的导航
Note:- All this code is automatically changed to kotlin (with some changes done manually ) , this issue was also with java and not after migrating to kotlin , Also if you want more reference of the code please tell me i will update the question
代码
MainActivity.kt // 底部导航
class MainActivity : AppCompatActivity() {
var bottomNavigationView: BottomNavigationView? = null
var integerDeque: Deque<Int> = ArrayDeque(3)
var flag = true
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
setContentView(R.layout.activity_main)
val window = this.window
window.statusBarColor = this.resources.getColor(R.color.black)
bottomNavigationView = findViewById(R.id.bottom_navigation_view)
integerDeque.push(R.id.nav_home)
loadFragments(Home_Fragment())
bottomNavigationView!!.selectedItemId = R.id.nav_home
bottomNavigationView!!.setOnNavigationItemSelectedListener(
BottomNavigationView.OnNavigationItemSelectedListener { item: MenuItem ->
val id = item.itemId
if (integerDeque.contains(id)) {
if (id == R.id.nav_home) {
integerDeque.size
if (flag) {
integerDeque.addFirst(R.id.nav_home)
flag = false
}
}
integerDeque.remove(id)
}
integerDeque.push(id)
loadFragments(getFragment(item.itemId))
false
}
)
}
@SuppressLint("NonConstantResourceId")
private fun getFragment(itemId: Int): Fragment {
when (itemId) {
R.id.nav_home -> {
bottomNavigationView!!.menu.getItem(0).isChecked = true
return Home_Fragment()
}
R.id.nav_following -> {
bottomNavigationView!!.menu.getItem(1).isChecked = true
return Following_Fragment()
}
R.id.nav_notification -> {
bottomNavigationView!!.menu.getItem(2).isChecked = true
return Notification_Fragment()
}
R.id.nav_profile -> {
bottomNavigationView!!.menu.getItem(3).isChecked = true
return Profile_Fragment()
}
}
bottomNavigationView!!.menu.getItem(0).isChecked = true
return Home_Fragment()
}
private fun loadFragments(fragment: Fragment?) {
if (fragment != null) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment, fragment.javaClass.simpleName)
.commit()
}
}
override fun onBackPressed() {
integerDeque.pop()
if (!integerDeque.isEmpty()) {
loadFragments(getFragment(integerDeque.peek()))
} else {
finish()
}
}
Edit_Profile.kt // 从这个片段我想回到最后一个应该是个人资料片段的片段
class Edit_Profile : Fragment() {
private var profilePhoto: CircleImageView? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_edit_profile, container, false)
profilePhoto = view.findViewById(R.id.circleImageView)
initImageLoader()
setProfileImage()
val imageView = view.findViewById<ImageView>(R.id.backArrow)
imageView.setOnClickListener {
val newCase: Fragment = Profile_Fragment()
assert(fragmentManager != null)
val transaction = requireFragmentManager().beginTransaction()
transaction.replace(R.id.fragment_container, newCase)
transaction.addToBackStack(Profile_Fragment.toString())
transaction.commit()
}
return view
}
编辑
添加了从个人资料片段到编辑个人资料的交易的一部分
ProfileFragment.kt
editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
val edit_profile: Fragment = Edit_Profile()
requireActivity().getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, edit_profile,"TAG")
.addToBackStack("TAG")
.commit()
})
通常我遵循这个模式
我在包含所有底部导航选项卡的主容器中添加 HomeF,所有底部导航选项卡都将在主容器中打开,而那些不属于底部导航的片段将在主容器中打开。我通常添加(而不是替换)主容器中的所有片段并设置添加到后台堆栈,这样如果用户从配置文件(home_container)转到主容器中的某些内容,而后台我们可以弹出顶部片段和用户将看到个人资料。
现在您正在通过 integerDeque
数组管理返回堆栈。
- 当您转到新的
BottomNavigationView
片段时;如果它不存在,你将它的 id 添加到数组中。 - 弹出返回栈时;顶部的片段被踢出数组。
但是由于您在 bottomNavigationView.setOnItemSelectedListener
回调中推送了所有这些 ID;那么 integerDeque
数组只包含 BottomNavigationView
个片段 ID。
并且由于 Edit_Profile
片段不是 BottomNavigationView
片段的一部分,因此它不会 added/popped 离开队列。相反,当您尝试在显示 Edit_Profile
片段时弹出返回堆栈时;您在 onBackPressed()
中管理的正常行为将继续,并且 Profile_Fragment
id 将从队列中弹出,使您 return 到您提到的示例中的前面片段 (Home_Fragment
)。
解决这个问题的一个小办法是考虑在处理 Edit_Profile
片段时将一个 id 添加到队列中,这样这个 id 就会从队列中弹出,从而返回到 Profile_Fragment
片段。
您可以使用片段的 ID 执行此操作以确保它是唯一的:
editProfileButton!!.setOnClickListener(View.OnClickListener { v: View? ->
val edit_profile: Fragment = Edit_Profile()
requireActivity().getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, edit_profile,"TAG")
.addToBackStack("TAG")
.commit()
(requireActivity() as MainActivity).integerDeque.push(id) // <<<< pushing id to the queue
})
这应该可以解决您的问题。
小贴士:
- 在
BNV
上使用setOnItemSelectedListener
而不是setOnNavigationItemSelectedListener
,因为后者已被弃用。 - Return
true
而不是setOnItemSelectedListener
回调中的false
因为这应该消耗事件并将BNV
标记为已选择。 - 在
Edit_Profile
事务中replace
片段而不是用add
添加片段,因为容器已经被消耗;这将使您避免容器中的重叠片段。 - 在
onBackPressed()
;您将loadFragments(..)
替换为bottomNavigationView.selectedItemId = integerDeque.peek()
;重用相同的片段而不是重做事务可能更轻松。