从 FragmentB 返回后,数据绑定对 FragmentA 不起作用

Data binding not working for FragmentA after returning from FragmentB

提问人:Reza Asadian 提问时间:9/25/2023 最后编辑:Reza Asadian 更新时间:9/27/2023 访问量:95

问:

我的应用程序有两个片段:和 . 将一些连接信息绑定到 UI。信息从广播接收器获取。一开始,数据绑定工作,但当用户转到 然后返回 时,info 不会绑定。HomeFragmentStatusFragmentHomeFragmentHomeFragmentStatusFragmentHomeFragment

HomeFragment、StatusFragment 和 HomeFragmentViewModel 类如下所示:

class HomeFragment : Fragment() {

    private var binding: FragmentHomeBinding? = null
    private var connectionStatus: String? = "Offline"
    private var connectionType: String? = ""

    private lateinit var mReceiver: ConnectivityChangeBroadcastReceiver
    lateinit var homeFragmentViewModel: HomeFragmentViewModel
    lateinit var repo: Repository

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

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentHomeBinding.inflate(inflater, container, false)
        binding?.lifecycleOwner = viewLifecycleOwner
        
        repo = Repository().getInstance()!!
        val viewModelFactory = HomeFragmentViewModelFactory(repo)
        homeFragmentViewModel =
            ViewModelProvider(this, viewModelFactory)[HomeFragmentViewModel::class.java]

        binding?.homeFragmentViewModel = homeFragmentViewModel
        this.mReceiver = ConnectivityChangeBroadcastReceiver()

        mReceiver.mData.observe(this, Observer {
            repo.setInfo(mReceiver.getData())
            homeFragmentViewModel.getConnectionInfo()
        })

        homeFragmentViewModel.navToStatusFragment.observe(this, Observer {
            if (it==true){
                this.findNavController().navigate(R.id.action_homeFragment_to_statusFragment)
                homeFragmentViewModel.doneNavigateToStatusFragment()
            }
        })

        return binding?.root
    }


    override fun onStart() {
        super.onStart()

        requireActivity().registerReceiver(
            mReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        )
        repo.setInfo(mReceiver.getData())
    }

    override fun onStop() {
        super.onStop()
        requireActivity().unregisterReceiver(mReceiver)
    }

}


class StatusFragment : Fragment() {

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

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        requireActivity().onBackPressedDispatcher.addCallback(this){
            requireActivity().supportFragmentManager.popBackStack()
        }
        return inflater.inflate(R.layout.fragment_status, container, false)
    }

}
class HomeFragmentViewModel(private val repo: Repository?) : ViewModel() {

    private val viewModelJob = Job()

    private val _navToStatusFragment = MutableLiveData<Boolean?>()
    val navToStatusFragment: LiveData<Boolean?>
        get() = _navToStatusFragment

    private val _connected = MutableLiveData<String?>()
    val connected: LiveData<String?>
        get() = _connected

    private val _connectionType = MutableLiveData<String?>()
    val connectionType: LiveData<String?>
        get() = _connectionType

    init {
        _navToStatusFragment.value = false
    }

    var isConnected: LiveData<Boolean> = connected.map {
        when (it) {
            "Online" -> true
            else -> false
        }
    }

    fun doneNavigateToStatusFragment(){
        _navToStatusFragment.value = false
    }

    fun navigateToStatusFragment(){
        _navToStatusFragment.value = true
    }

    fun getConnectionInfo() {

        when (repo?.getInfo()?.value) {
            0 -> {
                _connected.value = "Offline"
                _connectionType.value = "Check Your Connection"
                _connectionDetails.value = null
            }
            1 -> {
                _connected.value = "Online"
                _connectionType.value = "Cellular"
                _connectionDetails.value = null
            }
            2 -> {
                _connected.value = "Online"
                _connectionType.value = "WIFI"
                _connectionDetails.value = null
            }
            3 -> {
                _connected.value = "Online"
                _connectionType.value = "VPN"
                _connectionDetails.value = null
            }
        }
        Log.d("LOGGED", "Info Checked ...")
    }


    override fun onCleared() {
        super.onCleared()
        viewModelJob.cancel()
    }
}

我做了:

  • 在 中使用 也regiterReceiver()onResume()
  • using 代替 forviewLifeCycleOwnerthisbinding.lifeCycleOwner
  • 按下后退按钮时使用popBackStack()StatusFragment

更新:问题是回到 HomeFragment 后,调用了 onCreateView(),但未调用 HomeFragmentViewModelFactory()!->我需要它来发送新的存储库对象到视图模型

mvvm 数据绑定 广播接收器 android-viewbinding

评论


答:

0赞 mohsen sameti 9/27/2023 #1

关于您的更新说明:viewModel 在返回片段时不会重新创建,因此不会调用工厂。

您需要了解 viewModel Owner。为了创建一个 viewModel,我们需要一个 which 是一个接口,我们将在稍后看到,并且 , , 和类实现这个接口。这是第一个参数,在您的例子中是 this,这意味着当前片段。ViewModelStoreOwneractivityfragmentnavgraphnavBackStackEntryViewModelProvider

这有什么作用?基本上它有一个地图,它保留了创建的 viewModel 的实例,当请求 viewModel 使用时,首先它会查看该地图内部以查看它是否创建了它,如果创建了它,它将返回以前创建的 viewModel,否则它将调用 创建一个新的,之后它将其存储在该地图中以供以后使用。ViewModelStoreOwnerViewModelProviderViewModelFactory

那是什么意思?对于片段,此映射是在 onCreate 回调中创建的,并在 onDestroy 回调(不是 onDestroyView)中销毁,因此当您导航到新片段时,由于该片段尚未销毁,因此 viewModel 仍然存在于此映射中,因此在返回时,将返回之前创建的映射中的那个。

可能的解决方案:在 viewModel 中创建一个函数并将 repo 传递给它(而不是构造函数参数),或者根本不使用 viewModel!

评论

0赞 Reza Asadian 9/27/2023
是的,谢谢。它对我有用,但根据 MVVM 架构的原则,这种方式(使用函数)是否正确?
0赞 mohsen sameti 10/5/2023
当然为什么不呢,这些规则说在可能的情况下支持构造函数注入,但在你的情况下这是不可能的。从外部设置它是提供依赖关系的有效方式。特别是在你的情况下,以后可以坐它(因为你如何使用那个变量)