J'ai remarqué que dans la Android pour les fragments (notamment DialogFragment )), ils font deux ou trois choses différentes de ce à quoi je m'attendais:
1). Utilisez la méthode public static foo newInstance()
plutôt qu'un constructeur.
2). Passez des valeurs à onCreateDialog en utilisant setArguments plutôt que des variables membres.
J'ai lu que newInstance semble être préférable lors de l'utilisation de la réflexion. Cependant, je ne comprends vraiment pas pourquoi ils passent des paramètres via un bundle. J'aurais pensé que l'utilisation de variables membres serait plus sûre (n'utilisant pas de chaîne pour extraire une carte) et aurait moins de surcharge.
Des pensées?
J'ai également découvert cela et trouvé quelques avantages à utiliser les arguments Bundle
sur les champs d'instance:
S'il se trouve dans un Bundle
le Android le sait et peut créer et détruire votre Fragment
(en utilisant le constructeur sans paramètre/par défaut obligatoire et les méthodes de cycle de vie habituelles) , et passez à nouveau l'ensemble d'arguments. De cette façon, aucun argument n'est perdu lors d'une vague de destruction de mémoire ou des changements d'orientation éventuels (cela me frappe souvent lors du premier déploiement sur un appareil réel après le développement dans l'émulateur le moins rotatif).
Vous pouvez simplement passer les extras Bundle
d'un Activity
tel quel à un Fragment
incorporé dans la mise en page; par exemple. Je l'utilise souvent lorsque j'ai un Activity
qui affiche un Fragment
"plein écran" et a besoin d'un ID (ou ContentProvider
URI) pour savoir quoi afficher/faire. J'ajoute parfois même plus de choses à un Bundle
(ou une copie) avant de le transmettre, par ex.
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) { // not a re-creation
final Bundle args = new Bundle(getIntent().getExtras());
args.putInt(CoverImageFragment.BACKGROUND_RESOURCE, Android.R.color.black);
final Fragment fragment = CoverImageFragment.newInstance(args);
getSupportFragmentManager()
.beginTransaction()
.add(Android.R.id.content, fragment)
.commit();
}
}
Il garde la manière de développer un Fragment
proche de celui d'un Activity
, c'est-à-dire Bundle
en tant que "paramètres d'entrée, pas d'exceptions".
Quant aux inconvénients que vous avez mentionnés:
Je pense que la surcharge est minime parce que vous n'interrogerez probablement pas le Bundle
dans une boucle serrée, donc obtenir vos données d'argument une fois dans onCreate()
, onViewCreate()
, etc. n'est pas si mal.
Pour la sécurité des types, Bundle
a toutes les différentes méthodes getXXXX()
, et même des surcharges pour fournir une valeur par défaut si quelque chose manque/facultatif :)
Quant aux méthodes newInstance()
, je les considère comme un moyen facile d'encapsuler les appels new
et setArguments()
pour mes Fragment
; Je fournis parfois une MyFragment newInstance(String singleIdOfWhatToDisplay)
supplémentaire qui crée à la fois Bundle
et Fragment
en une seule fois et retourne une instance de Fragment
prête à l'emploi.
J'ai trouvé que c'était un problème TRÈS déroutant (l'un des nombreux qui jonchent le paysage Android)).
setArguments()
est une solution de contournement pour le besoin très inutile d'Android d'avoir un constructeur sans paramètre disponible pour Fragments.
Ma confusion est venue par vagues. Tout d'abord, les méthodes que vous remplacez naturellement dans votre Fragment
(par exemple onCreate
, onCreateView
) reçoivent un paramètre Bundle
qui représente le savedInstanceState
de votre Fragment
. Cet état d'instance n'a apparemment RIEN que ce soit à voir avec les valeurs que vous stockez via setArguments()
et récupérez via getArguments()
. Les deux utilisent un Bundle
, les deux Bundles
sont susceptibles d'être accédés dans la même méthode redéfinie, et n'ont rien à voir l'un avec l'autre.
Deuxièmement, on ne sait pas comment Android utilise setArguments()
. Android appelle votre constructeur sans paramètre pour reconstruire votre Fragment
sur rotation, mais apparemment, AUSSI appellera la dernière méthode setArguments()
appelée lors de la construction de Fragment
.
Huh ????
Incroyable, mais vrai. Tout cela créant Bundles
avec setArguments()
folie existe pour compenser le besoin d'un constructeur Fragment
sans paramètre.
En bref, j'utilise la méthode statique newInstance
pour créer mon Fragment
.
public MyFragment() {
//satisfy Android
}
public static MyFragment newInstance(long record_id) {
Log.d("MyFragment", "Putting " + record_id + " into newInstance");
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putLong("record_id", record_id);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Perform an immediate check of arguments,
* which ARE NOT the same as the bundle used
* for saved instance state.
*/
Bundle args = getArguments();
if(args != null) {
record_id = args.getLong("record_id");
Log.d("MyFragment", "found record_id of " + String.valueOf(record_id));
}
if(savedInstanceState != null) {
//now do something with savedInstanceState
}
}
Je suis assez nouveau dans la programmation Android mais c'est ma compréhension actuelle du problème:
Le constructeur de Fragments ne peut pas avoir des paramètres. Lorsque votre activité est suspendue, votre fragment peut être libéré. Avant que votre activité ne reprenne, le système crée une nouvelle version de votre fragment appelant le constructeur. Si un constructeur autre que celui par défaut est utilisé, comment Android est-il censé savoir quels sont les types et les valeurs des arguments de votre constructeur Fragments?
Je ne pense pas que ce bundle soit sorti. Le bundle est conservé avec précision afin qu'il puisse être renvoyé à votre fragment après avoir été recréé avec le constructeur par défaut.
Philipp Reichart y a échappé dans son message (en fait plus qu'échappé.)
Je veux juste ajouter un inconvénient de plus aux arguments, c'est que vous devez créer dynamiquement des fragments. Comme arguments ne fonctionne pas très bien si vous créez à partir du xml. Et je déteste vraiment ça.