Android
Fragments
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)
.
Navigation entre des fragments à l'aide d'un backstack et d'un motif de tissu statique
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>