Pourquoi est-ce que je veux éviter les constructeurs non par défaut dans les fragments?

173

Je crée une application avec Fragmentset dans l'un d'entre eux, j'ai créé un constructeur non par défaut et j'ai reçu cet avertissement:

Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead

Quelqu'un peut-il me dire pourquoi ce n'est pas une bonne idée?

Pouvez-vous également suggérer comment j'accomplirais ceci:

public static class MenuFragment extends ListFragment {
    public ListView listView1;
    Categories category;

    //this is my "non-default" constructor
    public MenuFragment(Categories category){
        this.category = category;
    }....

Sans utiliser le constructeur non par défaut?

NoirChapeauSamouraï
la source
3
Non, ça n'aide pas. Ils n'ont pas répondu à ma question. Mais merci
quand même
31
@BlaineOmega En fait, celui-ci en particulier: stackoverflow.com/a/11602478/321697 répond définitivement à votre question. Lors d'un changement d'orientation ou d'un autre événement qui provoque la recréation du fragment, Android utilise le constructeur par défaut ainsi que le bundle passé en argument. Si vous utilisez un constructeur personnalisé, dès que le fragment est recréé en raison de l'un de ces événements, tout ce que vous avez fait dans le constructeur personnalisé est perdu.
Kevin Coppock
1
Merci, mais cela répond au pourquoi, mais pas au comment.
BlackHatSamurai
Cela est couvert par les premier et deuxième liens dans mon commentaire initial.
CommonsWare

Réponses:

110

Créez un objet bundle et insérez vos données (dans cet exemple votre Categoryobjet). Attention, vous ne pouvez pas passer cet objet directement dans le bundle, sauf s'il est sérialisable. Je pense qu'il est préférable de créer votre objet dans le fragment et de ne mettre qu'un identifiant ou quelque chose d'autre dans le paquet. Voici le code pour créer et attacher un bundle:

Bundle args = new Bundle();
args.putLong("key", value);
yourFragment.setArguments(args);

Après cela, dans vos données d'accès au fragment:

Type value = getArguments().getType("key");

C'est tout.

nistv4n
la source
3
comment passer un objet? Je veux passer un objet de contexte ou tout autre objet.
Adil Malik
12
Les bundles peuvent transporter des objets Java sérialisés ainsi que des Parcelableobjets. De plus, vous ne devez pas passer de a Context, car ces informations sont accessibles via la getActivity()méthode du fragment .
krakatoa
En fragment où faire cela Type value = getArguments().getType("key");?
Muhammad Babar
4
@Muhammad Babar: Si j'étais vous, je l'ajouterais à la newInstance()méthode. Par exemple: public static FragmentName newInstance(your variables){}. Comme le recommande la documentation Android, ne faites pas de constructeur avec des paramètres, car celui par défaut (sans paramètres) sera appelé automatiquement après le redémarrage de votre fragment.
nistv4n
@MuhammadBabar onCreateView is OK
chanjianyi
272

Il semble qu'aucune des réponses ne réponde réellement "pourquoi utiliser un bundle pour passer des paramètres plutôt que des constructeurs non par défaut"

La raison pour laquelle vous devriez passer des paramètres via bundle est que lorsque le système restaure un fragment(par exemple lors d'un changement de configuration), il restaurera automatiquement votre fichier bundle.

Les rappels aiment onCreateou onCreateViewdevraient lire les paramètres de bundle- de cette façon, vous êtes assuré de restaurer fragmentcorrectement l'état du correctement dans le même état fragmentavec lequel il a été initialisé (notez que cet état peut être différent de celui onSaveInstanceState bundlequi est passé au onCreate/onCreateView)

La recommandation d'utiliser la newInstance()méthode statique n'est qu'une recommandation. Vous pouvez utiliser un constructeur non par défaut, mais assurez-vous de renseigner les paramètres d'initialisation à l' bundleintérieur du corps de ce constructeur. Et lisez ces paramètres dans les méthodes onCreate()ou onCreateView().

numan salati
la source
2
Bien expliqué. Merci. Si j'étais celui qui me posait la question, j'aurais donné une coche
Karue Benson Karue
5
Vous ne pouvez plus utiliser un constructeur autre que celui par défaut (pour une raison quelconque) .... il donne une erreur de compilation (utilisé pour être un avertissement).
MPavlak
51

Vous Fragmentne devriez pas avoir de constructeurs à cause de la façon dont ils l' FragmentManagerinstancient. Vous devez avoir une newInstance()méthode statique définie avec les paramètres dont vous avez besoin, puis les regrouper et les définir comme arguments du fragment, auxquels vous pourrez accéder ultérieurement avec le Bundleparamètre.

Par exemple:

public static MyFragment newInstance(int title, String message) {
    MyFragment fragment = new MyFragment();
    Bundle bundle = new Bundle(2);
    bundle.putInt(EXTRA_TITLE, title);
    bundle.putString(EXTRA_MESSAGE, message);
    fragment.setArguments(bundle);
    return fragment ;
}

Et lisez ces arguments sur onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    title = getArguments().getInt(EXTRA_TITLE);
    message = getArguments().getString(EXTRA_MESSAGE);

    //...
}

De cette façon, s'il est détaché et ré-attaché, l'état de l'objet peut être stocké via les arguments, un peu comme bundlesattaché à Intents.

Asaf Pinhassi
la source
9

Si vous utilisez un paramètre pour une classe. essaye ça

SomeClass mSomeInstance;
public static final MyFragment newInstance(SomeClass someInstance){
    MyFragment f = new MyFragment();
    f.mSomeInstance = someInstance;
    return f;
}
김동기
la source
5
C'est en fait une mauvaise suggestion. Une fois que Fragment sera recréé par a FragmentManager, vous perdrez mSomeInstance.
Yaroslav Mytkalyk
D'accord, SomeClass devrait être parcellable et stocké dans un paquet en utilisant setArguments ()
Jake_
1

Je pense qu'il n'y a pas de différence entre le constructeur statique et deux constructeurs (vide et paramétré qui stocke les arguments dans le paquet d'arguments d'un Fragment), très probablement, cette règle empirique est créée pour réduire la probabilité d'oublier d'implémenter le constructeur sans arg en Java , qui n'est pas généré implicitement en cas de surcharge.

Dans mes projets, j'utilise Kotlin et j'implémente des fragments avec un constructeur primaire sans argument et un constructeur secondaire pour les arguments qui les stocke simplement dans un bundle et le définit comme arguments Fragment, tout fonctionne bien.

Pavlus
la source
0

Si le fragment utilise des constructeurs autres que ceux par défaut après la modification de la configuration, le fragment perdra toutes les données.

Akop Vardanian
la source