我很难在我的应用程序中实现正确的导航结构。我希望它的行为类似于 YouTube 和 Instagram 中的导航。我遇到的最大问题是 backstack 和片段娱乐。
我目前正在使用具有多个片段方法的单个活动。我有一个应用栏和底部导航视图,在主要活动中设置了 3 个菜单项。应用栏有一个菜单项,在选中时导航到用户配置文件片段,并且底部导航的每个菜单项在选中时导航到不同的根片段(主页、搜索和配置文件)。我还使用 google 的 firebase 数据库和 firestore 来存储用户数据(电子邮件、uid、密码等)和照片。
我尝试使用 supportFragmentManager.beginTransaction().replace 方式和 android jetpack 的导航架构,但都无法产生我需要的结果。
我能够使用 supportFragmentManager 方式导航到正确的目的地,但似乎无法实现正确的向后导航结构。我试图找到实现它的其他代码示例,但找不到任何可行的方法,并且这些示例中的很多都是 Java 代码中的旧版本,带有不推荐使用的方法,这使得在尝试转换为 kotlin 代码时变得困难。
喷气背包导航组件更容易使用,但我也无法让它正常运行。据我所知,当前导航不支持多个 backstacks,并且没有适当的向后导航结构,除非您添加此处提供的 NavigationExtensions 文件:https ://github.com/googlesamples/android-architecture-components/tree/master/导航高级示例。使用此示例,我遇到了以下问题:
1.向后导航不会回到原来保存的fragment状态,而是重新创建一个全新的fragment。
2.从应用栏导航到配置文件片段有效,但是当用户在片段内并再次按下它时会崩溃。
3.将一组默认参数传递给底部导航视图中的用户片段项目菜单。我最初将帐户配置文件片段绑定到底部导航菜单项(仍用于测试目的),并将登录用户的 uid 设置为默认参数。使用的片段(UserFragment)采用 uid 参数并使用它从 google 的 firebase 获取正确的信息。我以前可以通过使用常规的喷气背包导航组件(没有高级示例)并在 MainActivity 中添加以下代码来实现这一点:
val navArgument1 = NavArgument.Builder().setDefaultValue(uid).build()
val orderDestination = navController.graph.findNode(R.id.user_Fragment)
orderDestination?.addArgument("destinationUid",navArgument1)
然后在用户片段中,我使用此代码获取正确的 uid:
uid = arguments?.getString("destinationUid")
使用高级示例导航组件,我无法将此默认参数传递给用户片段。我不断收到类似“没有与此片段关联的导航控制器”的错误消息,并且应用程序崩溃了。
主要活动
class ExploreActivity : AppCompatActivity(),BottomNavigationView.OnNavigationItemSelectedListener{
override fun onNavigationItemSelected(p0:MenuItem):Boolean{
when(p0.itemId){
R.id.home->{
val homeViewFragment = HomeViewFragment()
supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment,homeViewFragment).commit()
return true
}
R.id.world->{
val publicViewFragment = PublicViewFragment()
supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment,publicViewFragment).commit()
return true
}
R.id.account->{
val userFragment = UserFragment()
val bundle = Bundle()
val uid=FirebaseAuth.getInstance().currentUser?.uid
bundle.putString("destinationUid",uid)
userFragment.arguments=bundle
supportFragmentManager.beginTransaction().replace(R.id.nav_host_fragment,userFragment).commit()
return true
}
}
return false
}
override fun onCreate(savedInstanceState:Bundle?){
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_explore)
bottom_navigation_explore.setOnNavigationItemSelectedListener(this)
bottom_navigation_explore.selectedItemId=R.id.home
}
override fun onActivityResult(requestCode:Int,resultCode:Int,data:Intent?){
super.onActivityResult(requestCode,resultCode,data)
if(requestCode==UserFragment.PICK__PROFILE_FROM_ALBUM&&resultCode==Activity.RESULT_OK){
val imageUri=data?.data
val uid=FirebaseAuth.getInstance().currentUser?.uid
val storageRef=FirebaseStorage.getInstance().reference.child("userProfileImages")
.child(uid!!)
storageRef.putFile(imageUri!!).continueWithTask{task:Task<UploadTask.TaskSnapshot>->
return@continueWithTask storageRef.downloadUrl
}.addOnSuccessListener{uri->
val map=HashMap<String,Any>()
map["image"]=uri.toString()
FirebaseFirestore.getInstance().collection("profileImages").document(uid).set(map)
}
}
}
}
用户片段
class UserFragment : Fragment(){
var fragmentView : View? = null
var firestore : FirebaseFirestore? = null
var uid : String? = null
var auth : FirebaseAuth? = null
var currentUserUid : String? = null
companion object{
var PICK__PROFILE_FROM_ALBUM = 10
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
fragmentView = LayoutInflater.from(activity).inflate(R.layout.activity_main,container,false)
uid = arguments?.getString("destinationUid")
firestore = FirebaseFirestore.getInstance()
auth = FirebaseAuth.getInstance()
currentUserUid = auth?.currentUser?.uid
return fragmentView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(uid == currentUserUid) {
fragmentView?.btn_follow_signout_main?.text = "Signout"
fragmentView?.btn_follow_signout_main?.setOnClickListener {
activity?.finish()
startActivity(Intent(activity, LoginActivity::class.java))
auth?.signOut()
}
requestPermissions(arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),1)
iv_createpost_main.setOnClickListener {
if (ContextCompat.checkSelfPermission(context!!, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){
startActivity(Intent(activity,CreatePost::class.java))
}
return@setOnClickListener
}
//add explanation of why permission is needed
if (ContextCompat.checkSelfPermission(context!!, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
iv_profilepicture_main.setOnClickListener {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*"
activity?.startActivityForResult(intent, PICK__PROFILE_FROM_ALBUM)
}
}
}
else{
fragmentView?.btn_follow_signout_main?.text = "Follow +"
fragmentView?.btn_follow_signout_main?.setOnClickListener {
requestFollow()
}
}
getProfileImage()
getUserName()
}
private fun getProfileImage() {
firestore?.collection("profileImages")!!.document(uid!!).get().addOnCompleteListener { task ->
if(task.isSuccessful){
val url = task.result!!["image"]
if(url != null){
Glide.with(activity!!).load(url).into(iv_profilepicture_main)
}
else{
iv_profilepicture_main.setImageResource(R.drawable.ic_account)
}
}
}
}
private fun getUserName(){
firestore?.collection("users")!!.document(uid!!).get().addOnCompleteListener { task ->
if(task.isSuccessful){
val username = task.result!!["username"]
if(username != null){
activity?.setTitle("" + username)
}
}
}
}
}
我的项目目前正在使用支持片段管理器进行设置,但我一直在使用它和导航组件之间来回尝试让事情正常进行。
我还有两个与底部导航相关的片段,但我只包含了我认为我的问题所在的相关代码。其他两个片段有一个用户个人资料图片,当用户点击它时,它会将用户导航到选定的个人资料。我对这些事务没有任何问题,因为我可以使用 setOnClickListener 方法轻松应用包和参数。
TL;博士
总结一切:我正在寻找一种方法来在我的应用程序中实现正确的导航流程。我在向后导航和不应该重新创建片段时遇到问题。我尝试过使用片段管理器和 android jetpack 导航组件,但都没有运气。如果有人有任何关于如何使用 android kotlin 和最新方法实现这一点的信息,并愿意分享,我将不胜感激。
谢谢。