Cómo comunicarse con otros fragmentos

Para volver a usar los componentes de la UI de Fragment, debes crear cada uno como un componente modular completamente independiente que defina su propio diseño y comportamiento. Una vez que hayas definido esos elementos Fragment reutilizables, podrás asociarlos con un elemento Activity y conectarlos a la lógica de la aplicación para comprender la UI completa de la composición.

Con frecuencia, querrás que un elemento Fragment se comunique con otro (por ejemplo, para cambiar el contenido en función de un evento de usuario). Toda comunicación de Fragment a Fragment debe hacerse mediante un ViewModel compartido o mediante el elemento Activity asociado. Dos elementos Fragment nunca deben comunicarse directamente.

La forma recomendada de comunicar fragmentos es creando un objeto ViewModel compartido. Ambos fragmentos pueden acceder a ViewModel mediante el elemento Activity que los contiene. Los elementos Fragment pueden actualizar datos dentro de ViewModel y, si se expone la información utilizando LiveData, se enviará el nuevo estado al otro fragmento siempre y cuando esté observando LiveData desde ViewModel. Para ver cómo implementar este tipo de comunicación, lee la sección "Cómo compartir datos entre elementos Fragment" en la Guía de ViewModel.

Si no puedes utilizar un ViewModel compartido para establecer una comunicación entre tus elementos Fragment, puedes implementar manualmente un flujo mediante interfaces. Sin embargo, esto termina generando más trabajo para implementar y no se puede reutilizar fácilmente en otros fragmentos.

Cómo definir una interfaz

Para que un fragmento se comunique con su actividad, puedes definir una interfaz en la clase de fragmento e implementarla dentro del elemento Activity. El fragmento captura la implementación de la interfaz durante su método de ciclo de vida onAttach() y luego puede llamar a los métodos de la interfaz a fin de comunicarse con el elemento Activity.

Aquí te mostramos un ejemplo de comunicación de Fragment a Activity:

HeadlinesFragment

Kotlin

class HeadlinesFragment : ListFragment() {
    internal var callback: OnHeadlineSelectedListener

    fun setOnHeadlineSelectedListener(callback: OnHeadlineSelectedListener) {
        this.callback = callback
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    interface OnHeadlineSelectedListener {
        fun onArticleSelected(position: Int)
    }

    // ...
}

Java

public class HeadlinesFragment extends ListFragment {
    OnHeadlineSelectedListener callback;

    public void setOnHeadlineSelectedListener(OnHeadlineSelectedListener callback) {
        this.callback = callback;
    }

    // This interface can be implemented by the Activity, parent Fragment,
    // or a separate test implementation.
    public interface OnHeadlineSelectedListener {
        public void onArticleSelected(int position);
    }

    // ...
}

MainActivity

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    // ...

    fun onAttachFragment(fragment: Fragment) {
        if (fragment is HeadlinesFragment) {
            fragment.setOnHeadlineSelectedListener(this)
        }
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    // ...

    @Override
    public void onAttachFragment(Fragment fragment) {
        if (fragment instanceof HeadlinesFragment) {
            HeadlinesFragment headlinesFragment = (HeadlinesFragment) fragment;
            headlinesFragment.setOnHeadlineSelectedListener(this);
        }
    }
}

Ahora el fragmento puede enviar mensajes a la actividad invocando al método onArticleSelected() (u otros métodos en la interfaz) mediante la instancia mCallback de la interfaz OnHeadlineSelectedListener.

Por ejemplo, cuando el usuario hace clic en un elemento de la lista, se invoca al siguiente método del fragmento. El fragmento utiliza la interfaz de devolución de llamada para entregar el evento a la actividad primaria.

Kotlin

override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
    // Send the event to the host activity
    callback.onArticleSelected(position)
}

Java

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    // Send the event to the host activity
    callback.onArticleSelected(position);
}

Cómo implementar la interfaz

Para recibir devoluciones de llamadas de eventos por parte del fragmento, la actividad que lo aloja debe implementar la interfaz definida en la clase de fragmento.

Por ejemplo, la siguiente actividad implementa la interfaz del ejemplo anterior.

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    ...

    fun onArticleSelected(position: Int) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article
    }
}

Cómo enviar un mensaje a un fragmento

La actividad que aloja puede entregar mensajes a un fragmento mediante la captura de la instancia Fragment con findFragmentById() y luego invocar directamente a los métodos públicos del fragmento.

Por ejemplo, imagina que la actividad que se mostró anteriormente contiene otro fragmento que se usa para mostrar el elemento especificado por los datos devueltos en el método de devolución de llamada anterior. En ese caso, la actividad puede comunicar la información recibida en el método de devolución de llamada al otro fragmento que mostrará el elemento:

Kotlin

class MainActivity : Activity(), HeadlinesFragment.OnHeadlineSelectedListener {
    ...

    fun onArticleSelected(position: Int) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        val articleFrag = supportFragmentManager.findFragmentById(R.id.article_fragment) as ArticleFragment?

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position)
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...
            // Create fragment and give it an argument for the selected article
            val newFragment = ArticleFragment()
            val args = Bundle()
            args.putInt(ArticleFragment.ARG_POSITION, position)
            newFragment.arguments = args

            val transaction = supportFragmentManager.beginTransaction()

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment)
            transaction.addToBackStack(null)

            // Commit the transaction
            transaction.commit()
        }
    }
}

Java

public static class MainActivity extends Activity
        implements HeadlinesFragment.OnHeadlineSelectedListener{
    ...

    public void onArticleSelected(int position) {
        // The user selected the headline of an article from the HeadlinesFragment
        // Do something here to display that article

        ArticleFragment articleFrag = (ArticleFragment)
                getSupportFragmentManager().findFragmentById(R.id.article_fragment);

        if (articleFrag != null) {
            // If article frag is available, we're in two-pane layout...

            // Call a method in the ArticleFragment to update its content
            articleFrag.updateArticleView(position);
        } else {
            // Otherwise, we're in the one-pane layout and must swap frags...

            // Create fragment and give it an argument for the selected article
            ArticleFragment newFragment = new ArticleFragment();
            Bundle args = new Bundle();
            args.putInt(ArticleFragment.ARG_POSITION, position);
            newFragment.setArguments(args);

            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack so the user can navigate back
            transaction.replace(R.id.fragment_container, newFragment);
            transaction.addToBackStack(null);

            // Commit the transaction
            transaction.commit();
        }
    }
}

Para obtener más información sobre cómo implementar elementos Fragment, consulta Fragments. También puede obtener más detalles explorando la app de muestra correspondiente.