You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中两个不同Navigation Graph间的项目数据传递问题

问题描述

我的App里有两个独立的Navigation Graph:

  • 第一个包含三个Fragment:已创建项目列表Fragment、新建项目Fragment,以及嵌套了第二个Navigation Graph的SupportFragment;
  • 第二个Navigation Graph包含底部导航栏和项目详情Fragment,用户选中项目列表中的某一项时会激活这个导航图。

我用Room数据库存储项目信息,但不知道怎么把选中项目的数据传递到第二个Navigation Graph里:

  1. 本来想通过Navigation Graph的参数传值,但目标Fragment属于另一个导航图,不确定是否可行;
  2. 尝试过通过Room根据ID查询项目,但不知道代码层面怎么实现。

ProjectsDao

@Dao
interface ProjectsDao {
    @Insert
    suspend fun addProjects(projects: Projects)
    @Delete
    suspend fun deleteProject(projects: Projects)

    @Query("DELETE FROM projects")
    suspend fun deleteAllProjects()

    @Query("SELECT * FROM projects WHERE projects.id = :id")
    fun findProjectById(id: Int): LiveData<Projects>

    @Update
    suspend fun updateProject(projects: Projects)
    @Query("SELECT * FROM projects")
    fun readAllProjects(): LiveData<List<Projects>>
}

DataViewModel(已编辑)

class DataViewModel(application:Application): AndroidViewModel(application) {
     val readAllProjects: LiveData<List<Projects>>
     val currentProject: MutableLiveData<Projects> = MutableLiveData()

    private val repository: ProjectsRepository

    init{
        val projectsDao = ProjectsDatabase.getDatabase(application).projectsDao()
        repository = ProjectsRepository(projectsDao)
        readAllProjects = repository.readAllProjects
    }

    ....

    fun findProjectById(id: Int):LiveData<Projects>{
        return repository.findProjectById(id)
    }
    fun setCurrentItem(projects: Projects) {
        currentProject.value = projects
        //currentProject.postValue(projects)
    }
}

MainAdapter相关代码

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = projectsList[position]
        holder.binding.projectNameTxt.text = currentItem.pr_name

        setListeners(currentItem, holder)
        //holder.binding.rawLayout.setOnClickListener {
        //    val action = MainFragmentDirections.actionMainFragmentToSupportFragment(currentItem)
        //    holder.itemView.findNavController().navigate(action)
        //}
        //holder.binding.deleteProjectID.setOnClickListener{
        //    listener.deleteProject(currentItem)
        //}

    }

    private fun setListeners(currentItem: Projects, holder: MainAdapter.MyViewHolder) {
        holder.binding.rawLayout.setOnClickListener {
            val intent = Intent(holder.itemView.context, SupportFragment::class.java)
            intent.putExtra("id", currentItem.id)
            holder.itemView.context.startActivity(intent)
            //val action = MainFragmentDirections.actionMainFragmentToSupportFragment(intent)
            //holder.itemView.findNavController().navigate(action)
        }
        holder.binding.deleteProjectID.setOnClickListener{
            listener.deleteProject(currentItem)
        }
    }

SupportFragment代码

class SupportFragment : Fragment() {
    private val arg by navArgs<SupportFragmentArgs>()
    private var _binding: FragmentSupportBinding? = null
    lateinit var viewModel : DataViewModel
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding =  FragmentSupportBinding.inflate(inflater, container, false)
        viewModel = ViewModelProvider(this).get(DataViewModel::class.java)

        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val navHostFragment = childFragmentManager.findFragmentById(R.id.support) as NavHostFragment
        val navController = navHostFragment.findNavController()
        binding.bottomNav.setupWithNavController(navController)
        
        //Setting the current item value
        viewModel.setCurrentItem(arg.currentProject)

}

ProjectDetailsFragment代码

class ProjectDetailsFragment : Fragment() {
    lateinit var viewModel: DataViewModel
    private var _binding: FragmentProjectDetailsBinding? = null
    private val binding get() = _binding!!
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        _binding = FragmentProjectDetailsBinding.inflate(inflater, container, false)
        viewModel = ViewModelProvider(this).get(DataViewModel::class.java)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val setDataObserver = Observer<Projects>{project->
            binding.projectName.text = project.pr_name
        }
        //Observing currentProject
        viewModel.currentProject.observe(viewLifecycleOwner, setDataObserver)

}

解决方案

方法一:跨导航图传递参数(Navigation原生支持)

Navigation组件本身支持跨导航图传递参数,不用额外复杂操作,步骤如下:

  1. 修改MainAdapter的跳转逻辑
    放弃用Intent启动Fragment,改用Navigation的action跳转,直接传递项目ID:
private fun setListeners(currentItem: Projects, holder: MainAdapter.MyViewHolder) {
    holder.binding.rawLayout.setOnClickListener {
        // 生成跳转至SupportFragment的Action,传入项目ID
        val action = MainFragmentDirections.actionMainFragmentToSupportFragment(currentItem.id)
        holder.itemView.findNavController().navigate(action)
    }
    holder.binding.deleteProjectID.setOnClickListener{
        listener.deleteProject(currentItem)
    }
}
  1. 在导航图中配置参数
    在第一个导航图中,给actionMainFragmentToSupportFragment添加参数:
<action
    android:id="@+id/action_mainFragment_to_supportFragment"
    app:destination="@id/supportFragment">
    <argument
        android:name="projectId"
        app:argType="integer" />
</action>

此时SupportFragmentArgs会自动生成,不用手动修改。

  1. SupportFragment传递参数到子导航图
    在SupportFragment中拿到传递过来的ID,再传递给子导航图的ProjectDetailsFragment:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val navHostFragment = childFragmentManager.findFragmentById(R.id.support) as NavHostFragment
    val navController = navHostFragment.findNavController()
    binding.bottomNav.setupWithNavController(navController)

    // 获取传递的项目ID
    val projectId = arg.projectId
    // 如果需要直接跳转至详情页,传递参数
    val args = Bundle().apply {
        putInt("projectId", projectId)
    }
    // 若底部导航默认选中详情页,直接执行此跳转;否则可在选中详情页时传递
    navController.navigate(R.id.projectDetailsFragment, args)
}
  1. ProjectDetailsFragment接收参数并查询Room
    在详情页接收参数,通过ViewModel调用Room查询:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val projectId = arguments?.getInt("projectId") ?: return
    
    viewModel.findProjectById(projectId).observe(viewLifecycleOwner) { project ->
        binding.projectName.text = project.pr_name
    }
}

方法二:共享Activity级ViewModel

利用ViewModel的生命周期特性,使用Activity作为ViewModel的所有者,让SupportFragment和ProjectDetailsFragment共享同一个ViewModel实例,步骤如下:

  1. MainAdapter点击时设置ViewModel数据
    点击项目时,直接给Activity级别的ViewModel设置选中的项目:
private fun setListeners(currentItem: Projects, holder: MainAdapter.MyViewHolder) {
    holder.binding.rawLayout.setOnClickListener {
        // 获取Activity级别的ViewModel
        val viewModel = ViewModelProvider(holder.itemView.context as AppCompatActivity)[DataViewModel::class.java]
        viewModel.setCurrentItem(currentItem)
        
        // 跳转至SupportFragment
        val action = MainFragmentDirections.actionMainFragmentToSupportFragment()
        holder.itemView.findNavController().navigate(action)
    }
    holder.binding.deleteProjectID.setOnClickListener{
        listener.deleteProject(currentItem)
    }
}
  1. 修改ViewModel获取方式
    在SupportFragment和ProjectDetailsFragment中,都通过requireActivity()获取ViewModel,确保是同一个实例:
// SupportFragment中
viewModel = ViewModelProvider(requireActivity())[DataViewModel::class.java]

// ProjectDetailsFragment中
viewModel = ViewModelProvider(requireActivity())[DataViewModel::class.java]
  1. ProjectDetailsFragment观察数据
    此时直接观察ViewModel中的currentProject即可拿到选中的项目:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    viewModel.currentProject.observe(viewLifecycleOwner) { project ->
        binding.projectName.text = project.pr_name
    }
}

内容的提问来源于stack exchange,提问作者Bitaghd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 11:37:09