使用 ViewModel(Android Studio、Kotlin)将数据从片段 A 发送到片段 B
Sending data from fragment A to fragment B using ViewModel (Android Studio, Kotlin)
我的用例非常简单。我有两个片段需要使用 ViewModel 进行通信:
LoginFragment 有一个可编辑的电子邮件地址字段,其值需要在另一个名为 ProfileFragment 的片段中接收。字符串字段的直接传输。我的想法是从LoginFragment中输入邮箱地址,更新一个共享的ViewModel,然后在ProfileFragment中接收邮箱地址。
LoginFramgnet XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".LoginFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editTextTextEmailAddress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:autofillHints=""
android:backgroundTint="#FF9393"
android:ems="10"
android:hint="@string/email"
android:inputType="textEmailAddress"
android:textColor="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnLogin"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="#FFFDD2B5"
android:text="@string/login"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/editTextTextPassword"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/editTextTextPassword"
app:layout_constraintTop_toBottomOf="@+id/editTextTextPassword"
app:layout_constraintVertical_bias="0.37" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
LoginFragment.kt
class LoginFragment : Fragment() {
private lateinit var viewModel: LoginFragmentViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(LoginFragmentViewModel::class.java)
btnLogin.setOnClickListener {
Toast.makeText( activity,"${editTextTextEmailAddress.text} is logged in!!", Toast.LENGTH_SHORT).show()
viewModel.setEmailAddress("${editTextTextEmailAddress.text}")
Log.i("Info", "${viewModel.emailAddress.value}")
view.findNavController().navigate(R.id.action_loginFragment_to_profileFragment)
}
ViewModel.kt:
class LoginFragmentViewModel : ViewModel() {
val emailAddress = MutableLiveData<Any>()
fun setEmailAddress(email:String){
emailAddress.value = email
}
}
ProfileFragment XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProfileFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/txtEmailAddress"
android:layout_width="260dp"
android:layout_height="144dp"
android:text=""
android:textSize="14sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
ProfileFragment.kt:
class ProfileFragment : Fragment() {
private lateinit var viewModel: LoginFragmentViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(LoginFragmentViewModel::class.java)
viewModel.emailAddress.observe(viewLifecycleOwner, object : Observer<Any>{
override fun onChanged(t: Any?) {
txtEmailAddress.text = t!!.toString()
}
})
Log.i("Info", "${viewModel.emailAddress.value}")
}
根据在 editTextTextEmailAddress 中输入的内容,LoginFragment 上的日志记录了 ViewModel 中的 emailAddress 已更改。
ProfileFragment 上的日志 returns "null".
我到底做错了什么?
您有两个不同的 ViewModel
实例绑定到两个不同的 ViewModelStore
(表示为片段)。如果您需要共享 ViewModel
,则需要使用单个 ViewModelStore
。我相信将两个片段的 ViewModelProvider(this)
更改为 ViewModelProvider(requireActivity())
就可以解决问题,但是您需要自己找出它是否适合您的设计。
我的用例非常简单。我有两个片段需要使用 ViewModel 进行通信:
LoginFragment 有一个可编辑的电子邮件地址字段,其值需要在另一个名为 ProfileFragment 的片段中接收。字符串字段的直接传输。我的想法是从LoginFragment中输入邮箱地址,更新一个共享的ViewModel,然后在ProfileFragment中接收邮箱地址。
LoginFramgnet XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".LoginFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/editTextTextEmailAddress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="50dp"
android:layout_marginEnd="50dp"
android:autofillHints=""
android:backgroundTint="#FF9393"
android:ems="10"
android:hint="@string/email"
android:inputType="textEmailAddress"
android:textColor="#000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/btnLogin"
android:layout_width="200dp"
android:layout_height="50dp"
android:background="#FFFDD2B5"
android:text="@string/login"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/editTextTextPassword"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="@+id/editTextTextPassword"
app:layout_constraintTop_toBottomOf="@+id/editTextTextPassword"
app:layout_constraintVertical_bias="0.37" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>
LoginFragment.kt
class LoginFragment : Fragment() {
private lateinit var viewModel: LoginFragmentViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(LoginFragmentViewModel::class.java)
btnLogin.setOnClickListener {
Toast.makeText( activity,"${editTextTextEmailAddress.text} is logged in!!", Toast.LENGTH_SHORT).show()
viewModel.setEmailAddress("${editTextTextEmailAddress.text}")
Log.i("Info", "${viewModel.emailAddress.value}")
view.findNavController().navigate(R.id.action_loginFragment_to_profileFragment)
}
ViewModel.kt:
class LoginFragmentViewModel : ViewModel() {
val emailAddress = MutableLiveData<Any>()
fun setEmailAddress(email:String){
emailAddress.value = email
}
}
ProfileFragment XML:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ProfileFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/txtEmailAddress"
android:layout_width="260dp"
android:layout_height="144dp"
android:text=""
android:textSize="14sp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
ProfileFragment.kt:
class ProfileFragment : Fragment() {
private lateinit var viewModel: LoginFragmentViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(LoginFragmentViewModel::class.java)
viewModel.emailAddress.observe(viewLifecycleOwner, object : Observer<Any>{
override fun onChanged(t: Any?) {
txtEmailAddress.text = t!!.toString()
}
})
Log.i("Info", "${viewModel.emailAddress.value}")
}
根据在 editTextTextEmailAddress 中输入的内容,LoginFragment 上的日志记录了 ViewModel 中的 emailAddress 已更改。
ProfileFragment 上的日志 returns "null".
我到底做错了什么?
您有两个不同的 ViewModel
实例绑定到两个不同的 ViewModelStore
(表示为片段)。如果您需要共享 ViewModel
,则需要使用单个 ViewModelStore
。我相信将两个片段的 ViewModelProvider(this)
更改为 ViewModelProvider(requireActivity())
就可以解决问题,但是您需要自己找出它是否适合您的设计。