Buscar..


Introducción

Introducción a los fragmentos y su mecanismo de intercomunicación.

Sintaxis

  • void onActivityCreated (Bundle savedInstanceState) // Se invoca cuando se crea la actividad del fragmento y se crea una instancia de la jerarquía de vistas de este fragmento.

  • void onActivityResult (int requestCode, int resultCode, Intent data) // Recibe el resultado de una llamada anterior a startActivityForResult (Intent, int).

  • void onAttach (Actividad de actividad) // Este método ha quedado en desuso en el nivel 23 de la API. Utilice onAttach (Contexto) en su lugar.

  • void onAttach (contexto contextual) // Se invoca cuando un fragmento se adjunta por primera vez a su contexto.

  • void onAttachFragment (Fragment childFragment) // Se llama cuando un fragmento se adjunta como elemento secundario de este fragmento.

  • void onConfigurationChanged (Configuration newConfig) // Llamado por el sistema cuando la configuración del dispositivo cambia mientras su componente se está ejecutando.

  • void onCreate (Bundle savedInstanceState) // Llamado para hacer la creación inicial de un fragmento.

  • Ver onCreateView (inflador de LayoutInflater, contenedor ViewGroup, Bundle savedInstanceState) // Llamado para que el fragmento cree una instancia de su vista de interfaz de usuario.

  • void onDestroy () // Llamado cuando el fragmento ya no está en uso.

  • void onDestroyView () // Llamado cuando la vista creada previamente por onCreateView (LayoutInflater, ViewGroup, Bundle) se ha separado del fragmento.

  • void onDetach () // Llamado cuando el fragmento ya no está adjunto a su actividad.

  • void onInflate (Actividad, AttributeSet attrs, Bundle savedInstanceState) // Este método fue obsoleto en el nivel de API 23. Utilice onInflate (Context, AttributeSet, Bundle) en su lugar.

  • void onInflate (contexto de contexto, atributos AttributeSet, Bundle savedInstanceState) // Llamado cuando se crea un fragmento como parte de la inflación de un diseño de vista, generalmente desde la configuración de la vista de contenido de una actividad.

  • void onPause () // Llamado cuando el fragmento ya no se reanuda.

  • void onResume () // Llamado cuando el fragmento es visible para el usuario y se está ejecutando activamente.

  • void onSaveInstanceState (Bundle outState) // Llamado para pedirle al fragmento que guarde su estado dinámico actual, para que luego pueda reconstruirse en una nueva instancia de su proceso que se reinicie.

  • void onStart () // Llamado cuando el Fragmento es visible para el usuario.

  • void onStop () // Llamado cuando el Fragmento ya no se inicia.

  • void onViewStateRestored (Bundle savedInstanceState) // Llamado cuando todo el estado guardado se ha restaurado en la jerarquía de vistas del fragmento.

Observaciones

Un fragmento representa un comportamiento o una parte de la interfaz de usuario en una actividad. Puede combinar varios fragmentos en una sola actividad para crear una interfaz de usuario de múltiples paneles y reutilizar un fragmento en varias actividades. Puede pensar que un fragmento es una sección modular de una actividad, que tiene su propio ciclo de vida, recibe sus propios eventos de entrada y que puede agregar o eliminar mientras la actividad se está ejecutando (como una "subactividad" que puede reutilización en diferentes actividades).

Constructor

Cada fragmento debe tener un constructor vacío , por lo que se puede crear una instancia al restaurar el estado de su actividad. Se recomienda encarecidamente que las subclases no tengan otros constructores con parámetros, ya que estos constructores no se llamarán cuando se vuelva a crear una instancia del fragmento; en su lugar, los argumentos pueden ser proporcionados por el llamador con setArguments (Bundle) y luego recuperados por el Fragmento con getArguments ().

El patrón newInstance ()

Aunque es posible crear un constructor de fragmentos con parámetros, Android llama internamente al constructor de cero argumentos cuando vuelve a crear fragmentos (por ejemplo, si se restauran después de ser eliminados por razones propias de Android). Por esta razón, no es recomendable confiar en un constructor que tenga parámetros.

Para asegurarse de que sus argumentos de fragmentos esperados estén siempre presentes, puede usar un newInstance() estático newInstance() para crear el fragmento y colocar los parámetros que desee en un paquete que estará disponible cuando cree una nueva instancia.

import android.os.Bundle;
import android.support.v4.app.Fragment;

public class MyFragment extends Fragment
{
  // Our identifier for obtaining the name from arguments
  private static final String NAME_ARG = "name";

  private String mName;

  // Required
  public MyFragment(){}

  // The static constructor.  This is the only way that you should instantiate
  // the fragment yourself
  public static MyFragment newInstance(final String name) {
    final MyFragment myFragment = new MyFragment();
    // The 1 below is an optimization, being the number of arguments that will
    // be added to this bundle.  If you know the number of arguments you will add
    // to the bundle it stops additional allocations of the backing map.  If
    // unsure, you can construct Bundle without any arguments
    final Bundle args = new Bundle(1);

    // This stores the argument as an argument in the bundle.  Note that even if
    // the 'name' parameter is NULL then this will work, so you should consider
    // at this point if the parameter is mandatory and if so check for NULL and
    // throw an appropriate error if so
    args.putString(NAME_ARG, name);

    myFragment.setArguments(args);
    return myFragment;
  }

  @Override
  public void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    final Bundle arguments = getArguments();
    if (arguments == null || !arguments.containsKey(NAME_ARG)) {
      // Set a default or error as you see fit
    } else {
      mName = arguments.getString(NAME_ARG);
    }
  }
}

Ahora, en la Actividad:

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
MyFragment mFragment = MyFragment.newInstance("my name");
ft.replace(R.id.placeholder, mFragment);
//R.id.placeholder is where we want to load our fragment
ft.commit();

Este patrón es una práctica recomendada para garantizar que todos los argumentos necesarios se pasarán a los fragmentos en la creación. Tenga en cuenta que cuando el sistema destruye el fragmento y lo vuelve a crear más tarde, restaurará automáticamente su estado, pero debe proporcionarle una onSaveInstanceState(Bundle) .

En primer lugar, debemos agregar nuestro primer Fragment al principio, debemos hacerlo en el método onCreate() de nuestra Actividad:

if (null == savedInstanceState) {
    getSupportFragmentManager().beginTransaction()
      .addToBackStack("fragmentA")
      .replace(R.id.container, FragmentA.newInstance(), "fragmentA")
      .commit();
}

A continuación, tenemos que gestionar nuestro backstack. La forma más fácil es usar una función agregada en nuestra actividad que se usa para todas las Transacciones de fragmentos.

public void replaceFragment(Fragment fragment, String tag) {
    //Get current fragment placed in container
    Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);

    //Prevent adding same fragment on top
    if (currentFragment.getClass() == fragment.getClass()) {
        return;
    }

    //If fragment is already on stack, we can pop back stack to prevent stack infinite growth
    if (getSupportFragmentManager().findFragmentByTag(tag) != null) {
        getSupportFragmentManager().popBackStack(tag, FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }

    //Otherwise, just replace fragment
    getSupportFragmentManager()
            .beginTransaction()
            .addToBackStack(tag)
            .replace(R.id.container, fragment, tag)
            .commit();
}

Finalmente, deberíamos anular onBackPressed() para salir de la aplicación cuando regresemos del último Fragmento disponible en el backstack.

@Override
public void onBackPressed() {
    int fragmentsInStack = getSupportFragmentManager().getBackStackEntryCount();
    if (fragmentsInStack > 1) { // If we have more than one fragment, pop back stack
        getSupportFragmentManager().popBackStack();
    } else if (fragmentsInStack == 1) { // Finish activity, if only one fragment left, to prevent leaving empty screen
        finish();
    } else {
        super.onBackPressed();
    }
}

Ejecución en actividad:

replaceFragment(FragmentB.newInstance(), "fragmentB");

Ejecución fuera de la actividad (asumiendo que MainActivity es nuestra actividad):

((MainActivity) getActivity()).replaceFragment(FragmentB.newInstance(), "fragmentB");

Pasa los datos de la Actividad al Fragmento usando Bundle

Todos los fragmentos deben tener un constructor vacío (es decir, un método constructor que no tenga argumentos de entrada). Por lo tanto, para pasar sus datos al Fragmento que se está creando, debe usar el método setArguments() . Este método obtiene un paquete, en el que almacena sus datos, y almacena el paquete en los argumentos. Posteriormente, este paquete se puede recuperar en las onCreate() de onCreate() y onCreateView() del Fragmento.

Actividad:

 Bundle bundle = new Bundle();
 String myMessage = "Stack Overflow is cool!";
 bundle.putString("message", myMessage );
 FragmentClass fragInfo = new FragmentClass();
 fragInfo.setArguments(bundle);
 transaction.replace(R.id.fragment_single, fragInfo);
 transaction.commit();

Fragmento:

 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    String myValue = this.getArguments().getString("message");
    ...
 }

Enviar eventos de nuevo a una actividad con interfaz de devolución de llamada

Si necesita enviar eventos de fragmento a actividad, una de las posibles soluciones es definir la interfaz de devolución de llamada y requerir que la actividad del host lo implemente.

Ejemplo

Enviar devolución de llamada a una actividad, cuando se hace clic en el botón del fragmento

En primer lugar, definir la interfaz de devolución de llamada:
public interface SampleCallback {
    void onButtonClicked();
}

El siguiente paso es asignar esta devolución de llamada en el fragmento:

public final class SampleFragment extends Fragment {

    private SampleCallback callback;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof SampleCallback) {
            callback = (SampleCallback) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement SampleCallback");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        callback = null;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View view = inflater.inflate(R.layout.sample, container, false);
        // Add button's click listener
        view.findViewById(R.id.actionButton).setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                callback.onButtonClicked(); // Invoke callback here
            }
        });
        return view;
    }
}

Y finalmente, implementar callback en actividad:

public final class SampleActivity extends Activity implements SampleCallback {

    // ... Skipped code with settings content view and presenting the fragment

    @Override
    public void onButtonClicked() {
        // Invoked when fragment's button has been clicked
    }
}

Animar la transición entre fragmentos.

Para animar la transición entre fragmentos, o para animar el proceso de mostrar u ocultar un fragmento, utilice FragmentManager para crear un FragmentTransaction .

Para una FragmentTransaction individual, hay dos formas diferentes de realizar animaciones: puede usar una animación estándar o puede proporcionar sus propias animaciones personalizadas.

Las animaciones estándar se especifican llamando a FragmentTransaction.setTransition(int transit) y utilizando una de las constantes predefinidas disponibles en la clase FragmentTransaction . Al momento de escribir, estas constantes son:

FragmentTransaction.TRANSIT_NONE
FragmentTransaction.TRANSIT_FRAGMENT_OPEN
FragmentTransaction.TRANSIT_FRAGMENT_CLOSE
FragmentTransaction.TRANSIT_FRAGMENT_FADE

La transacción completa podría ser algo como esto:

getSupportFragmentManager()
    .beginTransaction()
    .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Las animaciones personalizadas se especifican llamando a FragmentTransaction.setCustomAnimations(int enter, int exit) o FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) .

Las animaciones de enter y exit se reproducirán para FragmentTransaction s que no impliquen fragmentos extraídos de la pila trasera. Las popEnter y popExit se reproducirán cuando se saque un fragmento de la pila trasera.

El siguiente código muestra cómo reemplazaría un fragmento al deslizar un fragmento y al otro en su lugar.

getSupportFragmentManager()
    .beginTransaction()
    .setCustomAnimations(R.anim.slide_in_left, R.anim.slide_out_right)
    .replace(R.id.contents, new MyFragment(), "MyFragmentTag")
    .commit();

Las definiciones de animación XML usarían la etiqueta objectAnimator . Un ejemplo de slide_in_left.xml podría verse así:

<?xml version="1.0" encoding="utf-8"?>
<set>
  <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:propertyName="x" 
    android:valueType="floatType"
    android:valueFrom="-1280"
    android:valueTo="0" 
    android:duration="500"/>
</set>

Comunicación entre fragmentos

Todas las comunicaciones entre Fragmentos deben ir a través de una Actividad. Los fragmentos NO PUEDEN comunicarse entre sí sin una Actividad.

Recursos adicionales

En este ejemplo, tenemos una MainActivity que aloja dos fragmentos, SenderFragment y ReceiverFragment , para enviar y recibir un message (una cadena simple en este caso) respectivamente.

Un botón en SenderFragment inicia el proceso de envío del mensaje. Un TextView en ReceiverFragment se actualiza cuando recibe el mensaje.

A continuación se encuentra el fragmento de MainActivity con comentarios que explican las líneas de código importantes:

// Our MainActivity implements the interface defined by the SenderFragment. This enables
// communication from the fragment to the activity
public class MainActivity extends AppCompatActivity implements SenderFragment.SendMessageListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

/**
 * This method is called when we click on the button in the SenderFragment
 * @param message The message sent by the SenderFragment
 */
@Override
public void onSendMessage(String message) {
    // Find our ReceiverFragment using the SupportFragmentManager and the fragment's id
    ReceiverFragment receiverFragment = (ReceiverFragment)
            getSupportFragmentManager().findFragmentById(R.id.fragment_receiver);

    // Make sure that such a fragment exists
    if (receiverFragment != null) {
        // Send this message to the ReceiverFragment by calling its public method
        receiverFragment.showMessage(message);
    }
}
}

El archivo de diseño de MainActivity aloja dos fragmentos dentro de un LinearLayout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.naru.fragmentcommunication.MainActivity">

<fragment
    android:id="@+id/fragment_sender"
    android:name="com.naru.fragmentcommunication.SenderFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_sender" />

<fragment
    android:id="@+id/fragment_receiver"
    android:name="com.naru.fragmentcommunication.ReceiverFragment"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    tools:layout="@layout/fragment_receiver" />
</LinearLayout>

El SenderFragment expone una interfaz SendMessageListener que ayuda a MainActivity saber cuándo se hizo clic en el botón en el SenderFragment .

A continuación se encuentra el fragmento de código para el SenderFragment explica las líneas importantes de código:

public class SenderFragment extends Fragment {

private SendMessageListener commander;

/**
 * This interface is created to communicate between the activity and the fragment. Any activity
 * which implements this interface will be able to receive the message that is sent by this
 * fragment.
 */
public interface SendMessageListener {
    void onSendMessage(String message);
}

/**
 * API LEVEL >= 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param context
 */
@Override
public void onAttach(Context context) {
    super.onAttach(context);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) context;
    } catch (ClassCastException e) {
        throw new ClassCastException(context.toString()
                + "must implement the SendMessageListener interface");
    }
}

/**
 * API LEVEL < 23
 * <p>
 * This method is called when the fragment is attached to the activity. This method here will
 * help us to initialize our reference variable, 'commander' , for our interface
 * 'SendMessageListener'
 *
 * @param activity
 */
@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    // Try to cast the context to our interface SendMessageListener i.e. check whether the
    // activity implements the SendMessageListener. If not a ClassCastException is thrown.
    try {
        commander = (SendMessageListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + "must implement the SendMessageListener interface");
    }
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize button and a click listener on it
    Button send = (Button) view.findViewById(R.id.bSend);
    send.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            // Sanity check whether we were able to properly initialize our interface reference
            if (commander != null) {

                // Call our interface method. This enables us to call the implemented method
                // in the activity, from where we can send the message to the ReceiverFragment.
                commander.onSendMessage("HELLO FROM SENDER FRAGMENT!");
            }
        }
    });

    return view;
}
}

El archivo de diseño para el SenderFragment :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">

<Button
    android:id="@+id/bSend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="SEND"
    android:layout_gravity="center_horizontal" />
</LinearLayout>

ReceiverFragment es simple y expone un método público simple para actualizar su TextView. Cuando MainActivity recibe el mensaje de SenderFragment , llama a este método público de ReceiverFragment

A continuación se muestra el fragmento de código para ReceiverFragment con comentarios que explican las líneas importantes de código:

public class ReceiverFragment extends Fragment {
TextView tvMessage;

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {
    // Inflate view for the sender fragment.
    View view = inflater.inflate(R.layout.fragment_receiver, container, false);

    // Initialize the TextView
    tvMessage = (TextView) view.findViewById(R.id.tvReceivedMessage);

    return view;
}


/**
 * Method that is called by the MainActivity when it receives a message from the SenderFragment.
 * This method helps update the text in the TextView to the message sent by the SenderFragment.
 * @param message Message sent by the SenderFragment via the MainActivity.
 */
public void showMessage(String message) {
    tvMessage.setText(message);
}
}

El archivo de diseño para el ReceiverFragment :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical">
<TextView
    android:id="@+id/tvReceivedMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Waiting for message!" />
</LinearLayout>


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow