Recherche…


Introduction

Introduction sur les fragments et leur mécanisme d'intercommunication

Syntaxe

  • void onActivityCreated (Bundle savedInstanceState) // Appelée lorsque l'activité du fragment a été créée et la hiérarchie de vue de ce fragment instanciée.

  • void onActivityResult (int requestCode, int resultCode, Intent data) // Reçoit le résultat d'un précédent appel à startActivityForResult (Intent, int).

  • void onAttach (activité d'activité) // Cette méthode est obsolète au niveau 23 de l'API. Utilisez plutôt onAttach (Context).

  • void onAttach (Contexte contextuel) // Appelé lorsqu'un fragment est attaché pour la première fois à son contexte.

  • void onAttachFragment (Fragment childFragment) // Appelé lorsqu'un fragment est attaché en tant qu'enfant de ce fragment.

  • void onConfigurationChanged (Configuration newConfig) // Appelé par le système lorsque la configuration du périphérique change pendant que votre composant est en cours d'exécution.

  • void onCreate (Bundle savedInstanceState) // Appelé pour faire la création initiale d'un fragment.

  • Voir onCreateView (inflater LayoutInflater, conteneur ViewGroup, Bundle savedInstanceState) // Appelé pour que le fragment instancie sa vue d'interface utilisateur.

  • void onDestroy () // Appelé lorsque le fragment n'est plus utilisé.

  • void onDestroyView () // Appelé lorsque la vue précédemment créée par onCreateView (LayoutInflater, ViewGroup, Bundle) a été détachée du fragment.

  • void onDetach () // Appelé lorsque le fragment n'est plus attaché à son activité.

  • Annulez onInflate (Activité, Attributs AttributeSet, Bundle savedInstanceState) // Cette méthode est obsolète au niveau 23 de l'API. Utilisez plutôt onInflate (Context, AttributeSet, Bundle).

  • Annulation de onInflate (Contexte contextuel, AttributeSet attrs, Bundle savedInstanceState) // Appelée lorsqu'un fragment est en cours de création dans le cadre d'une inflation de la disposition de la vue, généralement lors de la définition de la vue du contenu d'une activité.

  • void onPause () // Appelé lorsque le fragment n'est plus repris.

  • void onResume () // Appelé lorsque le fragment est visible par l'utilisateur et en cours d'exécution.

  • void onSaveInstanceState (Bundle outState) // Appelé pour demander au fragment de sauvegarder son état dynamique actuel, il peut ensuite être reconstruit dans une nouvelle instance de son processus est redémarré.

  • void onStart () // Appelé lorsque le fragment est visible par l'utilisateur.

  • void onStop () // Appelé lorsque le fragment n'est plus démarré.

  • void onViewStateRestored (Bundle savedInstanceState) // Appelé lorsque tout l'état enregistré a été restauré dans la hiérarchie de vue du fragment.

Remarques

Un fragment représente un comportement ou une partie de l'interface utilisateur dans une activité. Vous pouvez combiner plusieurs fragments dans une même activité pour créer une interface utilisateur à plusieurs volets et réutiliser un fragment dans plusieurs activités. Vous pouvez considérer un fragment comme une section modulaire d'une activité, qui possède son propre cycle de vie, reçoit ses propres événements d'entrée et que vous pouvez ajouter ou supprimer pendant l'exécution de l'activité (un peu comme une "sous-activité"). réutiliser dans différentes activités).

Constructeur

Chaque fragment doit avoir un constructeur vide , de sorte qu'il peut être instancié lors de la restauration de l'état de son activité. Il est fortement recommandé que les sous-classes n'aient pas d'autres constructeurs avec des paramètres, puisque ces constructeurs ne seront pas appelés lorsque le fragment sera ré-instancié; à la place, les arguments peuvent être fournis par l'appelant avec setArguments (Bundle) et récupérés plus tard par Fragment with getArguments ().

Le pattern newInstance ()

Bien qu'il soit possible de créer un constructeur de fragments avec des paramètres, Android appelle en interne le constructeur sans argument lors de la recréation de fragments (par exemple, s'ils sont restaurés après avoir été tués pour des raisons propres à Android). Pour cette raison, il est déconseillé de faire appel à un constructeur doté de paramètres.

Pour vous assurer que vos arguments de fragment attendus sont toujours présents, vous pouvez utiliser une newInstance() statique newInstance() pour créer le fragment et placer les paramètres souhaités dans un ensemble qui sera disponible lors de la création d'une nouvelle instance.

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

Maintenant, dans l'activité:

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

Ce modèle est une bonne pratique pour garantir que tous les arguments nécessaires seront transmis aux fragments lors de la création. Notez que lorsque le système détruit le fragment et le recrée plus tard, il restaure automatiquement son état - mais vous devez lui fournir une onSaveInstanceState(Bundle) .

Tout d'abord, nous devons ajouter notre premier Fragment au début, nous devrions le faire dans la méthode onCreate() de notre activité:

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

Ensuite, nous devons gérer notre backstack. La manière la plus simple consiste à utiliser une fonction ajoutée dans notre activité et utilisée pour toutes les FragmentTransactions.

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

Enfin, nous devrions remplacer onBackPressed() pour quitter l’application en revenant du dernier fragment disponible dans le 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();
    }
}

Exécution en activité:

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

Exécution en dehors de l'activité (en supposant que MainActivity est notre activité):

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

Passer des données d'Activité à Fragment en utilisant Bundle

Tous les fragments doivent avoir un constructeur vide (c.-à-d. Une méthode constructeur sans arguments d'entrée). Par conséquent, pour transmettre vos données au fragment en cours de création, vous devez utiliser la méthode setArguments() . Cette méthode obtient un ensemble dans lequel vous stockez vos données et stocke le bundle dans les arguments. Par la suite, cet ensemble peut être récupéré dans les onCreate() et onCreateView() du fragment.

Activité:

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

Fragment:

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

Envoi d'événements à une activité avec une interface de rappel

Si vous devez envoyer des événements de fragment en activité, l'une des solutions possibles consiste à définir l'interface de rappel et à exiger que l'activité hôte l'implémente.

Exemple

Envoyer un rappel à une activité lorsque le bouton du fragment est cliqué

Tout d'abord, définissez l'interface de rappel:
public interface SampleCallback {
    void onButtonClicked();
}

L'étape suivante consiste à assigner ce rappel dans fragment:

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;
    }
}

Et enfin, implémentez le rappel dans l'activité:

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

Animer la transition entre les fragments

Pour animer la transition entre les fragments ou pour animer le processus d'affichage ou de masquage d'un fragment, utilisez le FragmentManager pour créer une FragmentTransaction .

Pour une seule FragmentTransaction , il existe deux manières différentes d'effectuer des animations: vous pouvez utiliser une animation standard ou fournir vos propres animations personnalisées.

Les animations standard sont spécifiées en appelant FragmentTransaction.setTransition(int transit) et en utilisant l'une des constantes prédéfinies disponibles dans la classe FragmentTransaction . Au moment de la rédaction, ces constantes sont les suivantes:

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

La transaction complète pourrait ressembler à ceci:

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

Les animations personnalisées sont spécifiées en appelant soit FragmentTransaction.setCustomAnimations(int enter, int exit) ou FragmentTransaction.setCustomAnimations(int enter, int exit, int popEnter, int popExit) .

Les animations d’ enter et de exit seront jouées pour les FragmentTransaction qui n’impliquent pas l’extinction de fragments de la pile arrière. Les animations popEnter et popExit seront lues lors de l'extraction d'un fragment de la pile arrière.

Le code suivant montre comment remplacer un fragment en faisant glisser un fragment et en glissant l'autre à sa place.

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

Les définitions d'animation XML utiliseraient la balise objectAnimator . Un exemple de slide_in_left.xml pourrait ressembler à ceci:

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

Communication entre fragments

Toutes les communications entre les fragments doivent passer par une activité. Les fragments NE PEUVENT PAS communiquer entre eux sans une activité.

Ressources additionnelles

Dans cet exemple, nous avons une MainActivity qui héberge deux fragments, SenderFragment et ReceiverFragment , respectivement pour envoyer et recevoir un message (une chaîne simple dans ce cas).

Un bouton dans SenderFragment lance le processus d'envoi du message. Un TextView dans le ReceiverFragment est mis à jour lorsque le message est reçu par celui-ci.

Voici l'extrait de code pour MainActivity avec des commentaires expliquant les lignes de code 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);
    }
}
}

Le fichier de disposition de MainActivity héberge deux fragments à l'intérieur d'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>

SenderFragment expose une interface SendMessageListener qui aide le MainActivity savoir quand l' SenderFragment a cliqué sur le bouton dans SenderFragment .

Voici l'extrait de code pour le SenderFragment expliquant les lignes de code importantes:

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;
}
}

Le fichier de disposition pour le 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 est simple et expose une méthode publique simple pour mettre à jour son TextView. Lorsque MainActivity reçoit le message du SenderFragment il appelle cette méthode publique de ReceiverFragment

Voici l'extrait de code pour ReceiverFragment avec des commentaires expliquant les lignes de code importantes:

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

Le fichier de mise en page pour 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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow