📜  使用共享 ViewModel 在 Android 中进行片段到片段通信

📅  最后修改于: 2022-05-13 01:55:22.999000             🧑  作者: Mango

使用共享 ViewModel 在 Android 中进行片段到片段通信

如果一个活动中有两个或多个片段,它们需要在它们之间进行通信和共享数据。在两个片段之间共享数据的传统方式是使用一个接口来实现回调,这个接口很麻烦并且可能会抛出异常。但现代的做法是使用共享的 ViewModel。因此,在本文中,演示了如何使用共享的 ViewModel 在片段之间进行通信。查看下图以了解讨论的概况。

使用共享 ViewModel 在 Android 中进行片段到片段通信

先决条件

  • Android 中的片段生命周期
  • Android 架构组件中的 ViewModel

实现fragment之间通信的步骤

第 1 步:创建一个空的活动项目



创建一个空的活动 Android Studio 项目,并选择 Kotlin 作为编程语言。参考安卓 |如何在 Android Studio 中创建/启动新项目?。

第 2 步:添加所需的依赖项

依赖项包括 ViewModel 和 LiveData。因此,在应用级 Gradle 文件中添加以下依赖项并同步项目。

第 3 步:使用 activity_main.xml 文件

应用程序的主布局包含两个 FrameLayouts,它们包含两个片段。要实现相同的调用,请在activity_main.xml文件中调用以下代码。

XML


  
    
  
    
  
    
  
    
  
    
  


Kotlin
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
  
class SharedViewModel : ViewModel() {
  
    // Mutable LiveData which observed by LiveData 
      // and updated to EditTexts when it is changed.
    private val mutableLiveData: MutableLiveData = MutableLiveData()
  
    // function to set the changed
      // data from the EditTexts
    fun setData(input: CharSequence) {
        mutableLiveData.value = input
    }
  
    // function to get the changed data from the EditTexts
    fun getData(): MutableLiveData = mutableLiveData
}


XML


  
    
  
        
  
    
  
    


XML


  
    
  
        
  
    
  
    


Kotlin
import android.os.Bundle
import android.text.Editable
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
  
class Fragment1 : Fragment() {
  
    private var sharedViewModelInstance: SharedViewModel? = null
  
    private var editTextFromFragment1: EditText? = null
  
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View = inflater.inflate(R.layout.fragment_1, container, false)
  
        val sendDataButton: Button = view.findViewById(R.id.send_button_fragment_1)
        editTextFromFragment1 = view.findViewById(R.id.edit_text_from_fragment_1)
  
        // as soon as the button is clicked 
          // send the data to ViewModel
        // and the Live data will take care of
          // updating the data inside another Fragment
        sendDataButton.setOnClickListener {
            sharedViewModelInstance?.setData(editTextFromFragment1!!.text)
        }
  
        return view
    }
  
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
  
        // create instances of the shared view model 
          // when the activity is created
        sharedViewModelInstance = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
  
        // observe the data inside the view model that 
          // is mutable live of type CharSequence and 
          // set the data for edit text
        sharedViewModelInstance!!.getData().observe(viewLifecycleOwner, Observer {
            editTextFromFragment1!!.text = it as Editable?
        })
    }
}


Kotlin
import android.os.Bundle
import android.text.Editable
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
  
class Fragment2 : Fragment() {
  
    private var sharedViewModelInstance: SharedViewModel? = null
  
    private var editTextFromFragment2: EditText? = null
  
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View = inflater.inflate(R.layout.fragment_2, container, false)
  
        val sendDataButton: Button = view.findViewById(R.id.send_button_fragment_2)
        editTextFromFragment2 = view.findViewById(R.id.edit_text_from_fragment_2)
  
        // as soon as the button is clicked 
          // send the data to ViewModel
        // and the Live data will take care of 
          // updating the data inside another Fragment
        sendDataButton.setOnClickListener {
            sharedViewModelInstance?.setData(editTextFromFragment2!!.text)
        }
  
        return view
    }
  
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
  
        // create instances of the shared view model
          // when the activity is created
        sharedViewModelInstance = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
  
        // observe the data inside the view model that is mutable 
          // live of type CharSequence and set the data for edit text
        sharedViewModelInstance!!.getData().observe(viewLifecycleOwner, Observer {
            editTextFromFragment2!!.text = it as Editable?
        })
    }
}


Kotlin
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
  
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.apply {
            add(R.id.fragment_1_holder, Fragment1())
            add(R.id.fragment_2_holder, Fragment2())
            commit()
        }
    }
}


第 4 步:实现共享 ViewModel

SharedViewModel.kt文件中,有一个 CharSequence 的 MutableLiveData 用于设置 EditTexts 的数据。两个函数 setData 和 getData 用于在 EditTexts 中的数据发生更改时立即更新可变的实时数据。



使用共享 ViewModel 在 Android 中进行片段到片段通信

要实现相同的调用以下代码。

科特林

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
  
class SharedViewModel : ViewModel() {
  
    // Mutable LiveData which observed by LiveData 
      // and updated to EditTexts when it is changed.
    private val mutableLiveData: MutableLiveData = MutableLiveData()
  
    // function to set the changed
      // data from the EditTexts
    fun setData(input: CharSequence) {
        mutableLiveData.value = input
    }
  
    // function to get the changed data from the EditTexts
    fun getData(): MutableLiveData = mutableLiveData
}

第 5 步:创建 2 个片段

  • 创建两个具有自己布局的Fragment,命名为 Fragment1.kt 和 Fragment2.kt
  • 对于每个片段的布局,它包含一个 EditText 来获取要为片段 2 发送的数据和一个按钮,单击时它将数据共享给另一个片段。
  • 要实现 Fragment 1 的布局,请调用 fragment_1.xml 文件中的以下代码

XML



  
    
  
        
  
    
  
    

要实现 Fragment 2 的布局,请调用fragment_2.xml文件中的以下代码。

XML



  
    
  
        
  
    
  
    

第 6 步:在 Fragment.kt 文件中创建共享视图模型的实例

  • 创建Activity的时候需要创建ShareViewModel类型的ViewModel的实例。
  • 因此,需要覆盖每个 Fragment 中的 onActivityCreated() 方法。
  • 这是因为当我们看到片段的生命周期时,视图是在调用onCreateView()之后创建和更新的,并且在执行 onCreateView() 之后调用 onAcrivityCreated()回调。因此 LiveData 可以跟踪 UI 元素更新了哪些元素。请查看以下图表,了解片段的活动生命周期。

使用共享 ViewModel 在 Android 中进行片段到片段通信

要在两个片段中实现相同的内容,请调用Fragment1.kt 中的以下代码。



科特林

import android.os.Bundle
import android.text.Editable
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
  
class Fragment1 : Fragment() {
  
    private var sharedViewModelInstance: SharedViewModel? = null
  
    private var editTextFromFragment1: EditText? = null
  
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View = inflater.inflate(R.layout.fragment_1, container, false)
  
        val sendDataButton: Button = view.findViewById(R.id.send_button_fragment_1)
        editTextFromFragment1 = view.findViewById(R.id.edit_text_from_fragment_1)
  
        // as soon as the button is clicked 
          // send the data to ViewModel
        // and the Live data will take care of
          // updating the data inside another Fragment
        sendDataButton.setOnClickListener {
            sharedViewModelInstance?.setData(editTextFromFragment1!!.text)
        }
  
        return view
    }
  
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
  
        // create instances of the shared view model 
          // when the activity is created
        sharedViewModelInstance = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
  
        // observe the data inside the view model that 
          // is mutable live of type CharSequence and 
          // set the data for edit text
        sharedViewModelInstance!!.getData().observe(viewLifecycleOwner, Observer {
            editTextFromFragment1!!.text = it as Editable?
        })
    }
}

Fragment2.kt文件一样,调用以下代码。

科特林

import android.os.Bundle
import android.text.Editable
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
  
class Fragment2 : Fragment() {
  
    private var sharedViewModelInstance: SharedViewModel? = null
  
    private var editTextFromFragment2: EditText? = null
  
    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        val view: View = inflater.inflate(R.layout.fragment_2, container, false)
  
        val sendDataButton: Button = view.findViewById(R.id.send_button_fragment_2)
        editTextFromFragment2 = view.findViewById(R.id.edit_text_from_fragment_2)
  
        // as soon as the button is clicked 
          // send the data to ViewModel
        // and the Live data will take care of 
          // updating the data inside another Fragment
        sendDataButton.setOnClickListener {
            sharedViewModelInstance?.setData(editTextFromFragment2!!.text)
        }
  
        return view
    }
  
    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
  
        // create instances of the shared view model
          // when the activity is created
        sharedViewModelInstance = ViewModelProviders.of(activity!!).get(SharedViewModel::class.java)
  
        // observe the data inside the view model that is mutable 
          // live of type CharSequence and set the data for edit text
        sharedViewModelInstance!!.getData().observe(viewLifecycleOwner, Observer {
            editTextFromFragment2!!.text = it as Editable?
        })
    }
}

步骤 7:使用 MainActivity.kt 文件填充片段持有者

MainActivity.kt文件中,需要用两个片段填充 activity_main.xml 中的片段持有者。要实现相同的调用,请在MainActivity.kt文件中调用以下代码。

科特林

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.FragmentTransaction
  
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
  
        val fragmentTransaction: FragmentTransaction = supportFragmentManager.beginTransaction()
        fragmentTransaction.apply {
            add(R.id.fragment_1_holder, Fragment1())
            add(R.id.fragment_2_holder, Fragment2())
            commit()
        }
    }
}

输出:

想要一个更快节奏和更具竞争力的环境来学习 Android 的基础知识吗?
单击此处前往由我们的专家精心策划的指南,旨在让您立即做好行业准备!