Comunicación de fragmento a fragmento en Android usando ViewModel compartido

Si hay dos o más fragmentos en una actividad, necesitan comunicarse y compartir los datos entre ellos. La forma tradicional de compartir los datos entre los dos fragmentos es implementar la devolución de llamada mediante una interfaz que es engorrosa y puede generar excepciones. Pero la forma moderna de hacerlo es usar ViewModel compartido. Entonces, en este artículo, se demostró cómo se puede usar el ViewModel compartido para comunicarse entre los fragmentos. Eche un vistazo a la siguiente imagen para obtener una descripción general de la discusión.

Fragment to Fragment Communication in Android using Shared ViewModel

Nota: Esta discusión se implementa en el lenguaje de programación Kotlin .

requisitos previos

Pasos para implementar la comunicación entre fragmentos

Paso 1: crear un proyecto de actividad vacío

Cree un proyecto de Android Studio de actividad vacío y seleccione Kotlin como lenguaje de programación. Consulte Android | ¿Cómo crear/comenzar un nuevo proyecto en Android Studio? .

Paso 2: agregar las dependencias requeridas

Las dependencias incluyen ViewModel y LiveData. Entonces, en el archivo Gradle del nivel de la aplicación, agregue las siguientes dependencias y sincronice el proyecto.

// lifecycle_version y las versiones de arquitectura pueden variar
def lifecycle_version = “2.3.1”

def arch_version = “2.1.0”

// Ver modelo

implementación “androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version”

// Datos en tiempo real

implementación “androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version”

// Solo ciclos de vida (sin ViewModel o LiveData)

implementación “androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version”

// arquitectura Extensiones del ciclo de vida

implementación «androidx.lifecycle:lifecycle-extensions:$arch_version»

Paso 3: trabajar con el archivo activity_main.xml

El diseño principal de la aplicación contiene los dos FrameLayouts que contienen los dos fragmentos. Para implementar lo mismo, invoque el siguiente código dentro del archivo activity_main.xml .

XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    tools:ignore="HardcodedText">
  
    <TextView
        android:id="@+id/textView"
        style="@style/TextAppearance.MaterialComponents.Headline6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="Fragment 1:"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
    <FrameLayout
        android:id="@+id/fragment_1_holder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />
  
    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_marginTop="16dp"
        android:background="@color/green_700"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/fragment_1_holder" />
  
    <TextView
        android:id="@+id/textView2"
        style="@style/TextAppearance.MaterialComponents.Headline6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="16dp"
        android:text="Fragment 2: "
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view" />
  
    <FrameLayout
        android:id="@+id/fragment_2_holder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView2" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

Paso 4: Implementación de ViewModel compartido

En el archivo SharedViewModel.kt hay un MutableLiveData de CharSequence para configurar los datos para EditTexts . Dos funciones setData y getData para actualizar esos datos en vivo mutables tan pronto como cambien los datos dentro de EditTexts.

Fragment to Fragment Communication in Android using Shared ViewModel

Para implementar lo mismo, invoque el siguiente código.

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<CharSequence> = 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<CharSequence> = mutableLiveData
}

Paso 5: Creando 2 Fragmentos

  • Cree dos Fragmentos con sus propios diseños nombrándolos Fragment1.kt y Fragment2.kt .
  • Para cada uno de los diseños del fragmento, contiene un EditText para obtener los datos para enviar para el fragmento 2 y un botón, cuando se hace clic, comparte los datos con otro fragmento.
  • Para implementar el diseño del Fragmento 1, invoque el siguiente código dentro del archivo fragment_1.xml

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    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"
    android:clipToPadding="false"
    android:padding="4dp"
    tools:context=".Fragment1"
    tools:ignore="HardcodedText">
  
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/edit_text_layout_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
  
        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/edit_text_from_fragment_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter the data" />
  
    </com.google.android.material.textfield.TextInputLayout>
  
    <Button
        android:id="@+id/send_button_fragment_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_text_layout_1"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:text="Send Data to Fragment 2" />
  
</RelativeLayout>

Para implementar el diseño del Fragmento 2, invoque el siguiente código dentro del archivo fragment_2.xml .

XML

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 
    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"
    android:clipToPadding="false"
    android:padding="4dp"
    tools:context=".Fragment2"
    tools:ignore="HardcodedText">
  
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/edit_text_layout_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
  
        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/edit_text_from_fragment_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter the data" />
  
    </com.google.android.material.textfield.TextInputLayout>
  
    <Button
        android:id="@+id/send_button_fragment_2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/edit_text_layout_2"
        android:layout_alignParentEnd="true"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        android:text="Send Data to Fragment 1" />
  
</RelativeLayout>

Paso 6: Creación de instancias del modelo de vista compartida dentro de los archivos Fragment.kt

  • Es necesario crear la instancia de ViewModel del tipo ShareViewModel cuando se crea la Actividad.
  • Entonces, uno debe anular el método onActivityCreated() dentro de cada uno de los Fragmentos.
  • Esto es así porque cuando vemos el ciclo de vida de los fragmentos, las vistas se crean y actualizan después de que se llama a onCreateView() y se llama a la devolución de llamada onAcrivityCreated() después de que se ejecuta onCreateView(). Por lo tanto, LiveData puede realizar un seguimiento de los elementos de la interfaz de usuario para los cuales se han actualizado. Eche un vistazo al siguiente gráfico para ver el ciclo de vida de la actividad del fragmento.

Fragment to Fragment Communication in Android using Shared ViewModel

Para implementar lo mismo en ambos fragmentos, invoque el siguiente código dentro de Fragment1.kt .

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?
        })
    }
}

Y lo mismo ocurre con el archivo Fragment2.kt , invoque el siguiente código.

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?
        })
    }
}

Paso 7: rellene los titulares de fragmentos utilizando el archivo MainActivity.kt

Dentro del archivo MainActivity.kt , es necesario rellenar los titulares de fragmentos de activity_main.xml, con ambos fragmentos. Para implementar lo mismo, invoque el siguiente código dentro del archivo MainActivity.kt .

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()
        }
    }
}

Producción:

Publicación traducida automáticamente

Artículo escrito por adityamshidlyali y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *