首页 > 解决方案 > 如何从 ViewModel 中的 Fragment 访问 Android 数据

问题描述

我正在学习 Android 编程,但我不知道如何在 Fragment 和 ViewModel 之间共享数据。

我正在制作应用程序,该应用程序将在设置中包含IP 地址IP 端口字段。

在起始页片段中,我创建了fillServerData()将数据从设置加载到serverViewModel对象的函数。

我已经在 Fragment 中创建了 ViewModel 对象,它可以正常工作。我可以在任何片段中启动它:

serverViewModel = activity?.run {
            ViewModelProvider(this).get(ServerObservableData::class.java)
        } ?: throw Exception("Invalid Activity")

并使用 . 访问每个字段serverViewModel.ipAddress.value.toString()

我在访问 ViemModel.kt 中的数据时遇到问题,我无法使用

activity?.run {
            ViewModelProvider(this).get(ServerObservableData::class.java)
        } ?: throw Exception("Invalid Activity")

我的问题是 - 我如何访问serverViewModel片段外的数据?有什么方法可以在 ViewModel 中创建可以读取数据的对象?我可以使用使用数据绑定吗?

编辑:

我希望我的应用程序连接到服务器,以便它可以检索数据。

我创建了设置片段,用户将在哪里填写服务器、端口、用户名和密码数据。

我创建了 class: ServerObservableData : ViewModel(),并且在我的起始页片段上有 function fillServerData(),它将用于SharedPreferences收集值并将它们传递给serverViewModel对象。这很好用。我可以从serverViewModel任何片段中获取数据。我只需要使用以下方法对其进行初始化:

serverViewModel = activity?.run {
            ViewModelProvider(this).get(ServerObservableData::class.java)
        } ?: throw Exception("Invalid Activity")

我的问题是serverViewModel存储在对象中的数据SharedPreferences,我需要在 ViewModel 中使用,而不是 Fragment。如何将它们从 Fragment 传递到 ViewModel?

Kamal Nayan 的建议对我来说是可以的,但由于某种原因程序崩溃并且消息是:

UninitializedPropertyAccessException:lateinit 属性 serverViewModel 尚未初始化

但是当我放置Log.i()函数以便跟踪正在发生的事情时,会调用 onCreate(),并且serverViewModel对象的初始化在该函数中。所以程序不应该崩溃,因为onCreate()之前已经调用过get()

编辑二:

这是 DocumentsFragment.kt 中的代码:

class DocumentsFragment : Fragment() {
    private lateinit var serverViewModel: ServerObservableData
    private val viewModel: DocumentsViewModel by lazy {
        ViewModelProvider(this).get(DocumentsViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        serverViewModel = activity?.run {
            ViewModelProvider(this).get(ServerObservableData::class.java)
        } ?: throw Exception("Invalid Activity")

        Log.i("DocumentsFragment", "onCreate")
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val binding: FragmentDocumentsBinding = DataBindingUtil.inflate(
            inflater,
            R.layout.fragment_documents,
            container,
            false
        )

        binding.lifecycleOwner = this

        binding.documentViewModel = viewModel

        return binding.root
    }

    fun getHttp(): String {
        Log.i("getHTTP", "RETURN")
        // THIS IS WHERE PROGRAM CRASHES, WHEN RETURN IS CALLED
        // ...serverViewModel has not been initialized
        return serverViewModel.ipAddress.value.toString()
    }

    fun getIP(): String {
        return serverViewModel.ipPort.value.toString()
    }
}

DocumentsViewModel.kt 中的代码

class DocumentsViewModel : ViewModel() {
    private val _response = MutableLiveData<String>()
    val response: LiveData<String>
        get() = _response

    init {
        Log.i("ViewModel", "ViewModel INIT")
        testConnection()
    }

    private fun testConnection() {
        var baseUrl = "http://${DocumentsFragment().getHttp()}:${DocumentsFragment().getIP()}/rest/out"

        val orgInfo = RequestBody(
            user = "test",
            request = "x",
            signature = "x",
            requestJson = "x"
        )

        viewModelScope.launch {
            try {
                val listResult =
                    ElementApi.retrofitService.sendRequest(baseUrl, orgInfo)
                _response.value = "Success."
            } catch (e: Exception) {
                _response.value = "Failure: ${e.message.toString()}"
            }
        }
    }
}

标签: androidkotlin

解决方案


** 我不是 Kotlin 人。** 但是我在 java 中使用 setter 和 getter,比如在 viewModel 中创建一个变量并创建它的 getter 和 setter。并使用 getter 从 ViewModel 外部(例如在片段中)访问该变量数据,并使用 setter 从 viewModel 外部设置 viewModel 内部变量的值。


推荐阅读