Cómo comunicar contenido con otros fragmentos

Si deseas volver a usar los componentes de la IU de objetos 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 objetos Fragment reutilizables, podrás asociarlos con un objeto Activity y conectarlos a la lógica de la aplicación para comprender la IU completa de la composición.

Con frecuencia, querrás que un objeto Fragment se comunique con otro; por ejemplo, para cambiar el contenido en función de un evento de usuario. Toda la comunicación entre objetos Fragment se realiza mediante un ViewModel compartido o mediante el elemento Activity asociado. Dos objetos Fragment nunca deben comunicarse directamente.

La forma recomendada de comunicación entre fragmentos es crear un objeto ViewModel compartido. Ambos fragmentos pueden acceder al objeto ViewModel mediante el objeto Activity que los contiene. Los objetos Fragment pueden actualizar los datos dentro del ViewModel y, si los datos se exponen mediante LiveData, el estado nuevo se enviará al otro fragmento siempre que esté observando LiveData desde el ViewModel. Para ver cómo implementar este tipo de comunicación, consulta la sección "Cómo compartir los datos entre fragmentos" en la guía de ViewModel.

Si no puedes utilizar un ViewModel compartido para la comunicación entre objetos Fragment, puedes implementar un flujo de comunicación manualmente mediante interfaces. Sin embargo, esto genera un mayor esfuerzo de implementación y no puede reutilizarse en otros objetos Fragment con facilidad.

Cómo definir una interfaz

Para permitir que un objeto Fragment se comunique con su Activity, puedes definir una interfaz en la clase Fragment y luego implementarla dentro del objeto Activity. El objeto Fragment 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 para comunicarse con el objeto Activity.

Este es un ejemplo de comunicación entre objetos Fragment y 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 llamar al método onArticleSelected() (o a otros métodos en la interfaz) mediante la instancia mCallback de la interfaz OnHeadlineSelectedListener para enviar mensajes a la actividad.

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

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 host puede entregar mensajes a un fragmento mediante la captura de la instancia Fragment con findFragmentById() y, luego, llamar 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 generados 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 de la siguiente manera:

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...
            supportFragmentManager.commit {
                // Replace whatever is in the fragment_container view with a new Fragment, generated
                // from the FragmentFactory, and give it an argument for the selected article
                replace<ArticleFragment>(R.id.fragment_container, null, bundleOf(ArticleFragment.ARG_POSITION to position))
                // add the transaction to the back stack so the user can navigate back
                addToBackStack(null)
            }
        }
    }
}

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 la implementación de objetos Fragment, consulta Fragmentos. También puedes obtener más información explorando la app de muestra de FragmentBasics.