775 votes

Meilleure pratique pour l'instanciation d'un nouveau fragment d'Android

J'ai vu deux pratiques générales pour instancier un nouveau Fragment dans une application :

Fragment newFragment = new MyFragment();

y

Fragment newFragment = MyFragment.newInstance();

La deuxième option fait appel à une méthode statique newInstance() y généralement contient la méthode suivante.

public static Fragment newInstance() 
{
    MyFragment myFragment = new MyFragment();
    return myFragment;
}

Au début, je pensais que le principal avantage était le fait que je pouvais surcharger la méthode newInstance() afin de donner de la flexibilité lors de la création de nouvelles instances d'un Fragment - mais je pouvais aussi le faire en créant un constructeur surchargé pour le Fragment.

J'ai raté quelque chose ?

Quels sont les avantages d'une approche par rapport à une autre ? Ou s'agit-il simplement d'une bonne pratique ?

0 votes

Lorsqu'il y a des paramètres, il n'y a pas de choix, et cette question est largement traitée ici. Cependant, la question reste posée pour la construction sans argument du fragment.

1 votes

Après avoir appris le factory pattern et comment une classe appelante qui n'instancie pas elle-même un objet aide à les découpler, j'ai pensé que ce serait un point fort pour la méthode newInstance(). Ai-je tort ? Je n'ai pas vu cet argument spécifique parlé comme un avantage.

1251voto

yydl Points 6552

Si Android décide de recréer votre fragment plus tard, il va appeler le constructeur sans argument de votre fragment. Ainsi, surcharger le constructeur n'est pas une solution.

Ceci étant dit, la façon de passer des éléments à votre Fragment afin qu'ils soient disponibles après qu'un Fragment soit recréé par Android est de passer un paquet à la balise setArguments méthode.

Ainsi, par exemple, si nous voulions passer un entier au fragment, nous utiliserions quelque chose comme :

public static MyFragment newInstance(int someInt) {
    MyFragment myFragment = new MyFragment();

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    myFragment.setArguments(args);

    return myFragment;
}

Et plus loin dans le Fragment onCreate() vous pouvez accéder à cet entier en utilisant :

getArguments().getInt("someInt", 0);

Cet ensemble sera disponible même si le fragment est en quelque sorte recréé par Android.

A noter également : setArguments ne peut être appelé qu'avant que le Fragment soit attaché à l'Activité.

Cette approche est également documentée dans la référence du développeur Android : https://developer.Android.com/reference/Android/app/Fragment.html

2 votes

Alors ils auraient dû faire newInstance() pour que nous puissions surcharger.

7 votes

@Vlasto malheureusement les méthodes statiques ne peuvent pas être surchargées.

11 votes

@yydl Je pense que je manque quelque chose ici, ne pourriez-vous pas utiliser un constructeur ici de toute façon, un qui crée le Bundle et appelle setArguments() encore puisqu'il sera seulement appelé par votre code (et pas quand Android recrée votre fragment) ?

101voto

500865 Points 3045

Le seul avantage d'utiliser le newInstance() que je vois sont les suivants :

  1. Vous aurez un seul endroit où tous les arguments utilisés par le fragment pourraient être regroupés et vous ne devez pas écrire le code ci-dessous chaque fois que vous instanciez un fragment.

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    args.putString("someString", someString);
    // Put any other arguments
    myFragment.setArguments(args);
  2. C'est un bon moyen de dire aux autres classes quels sont les arguments qu'elles utilisent. s'attend à ce que pour fonctionner fidèlement (bien que vous devriez être capable de gérer les cas où aucun argument n'est regroupé dans l'instance du fragment).

Donc, je pense que l'utilisation d'un newInstance() pour instancier un fragment est une bonne pratique.

7 votes

1) En quoi cela est-il différent de mettre la logique dans un constructeur ? Les deux sont des endroits uniques où vous incluez cette logique. 2) En quoi les paramètres d'une fabrique statique sont-ils différents des paramètres d'un constructeur ? Les deux indiquent quels arguments sont attendus. Ce que je veux dire, c'est que c'est un paradigme différent, bien sûr, mais il n'y a pas d'avantage clair à cela par rapport à l'utilisation des constructeurs.

4 votes

Vous ne pouvez pas utiliser de constructeurs personnalisés pour fragment. Framework utilise le constructeur sans argument pour restaurer les fragments.

6 votes

Oui, je suis d'accord avec vous. Je dis que, d'un point de vue conceptuel, il n'y a aucun avantage à utiliser le modèle de fabrique statique au lieu d'utiliser des constructeurs surchargés, et vice versa. Vos deux points sont valables pour les deux patterns ; il n'y a aucun avantage à utiliser l'un plutôt que l'autre. Android vous oblige à utiliser le modèle de fabrique statique - mais il n'y a aucun avantage à utiliser l'un ou l'autre.

68voto

user1145201 Points 316

Il y a aussi un autre moyen :

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)

0 votes

Si je ne me trompe pas, cela n'est possible que si vous utilisez la bibliothèque Android Support.

0 votes

C'est également possible sans la bibliothèque de soutien.

2 votes

J'ai essayé cela avec la bibliothèque de support, mais dans onCreateView (dans mon fragment), le paquet passé était nul, donc j'ai utilisé l'option setArguments/getArguments et cela a fonctionné (pour tous ceux qui lisent ceci).

55voto

ps95 Points 2130

Alors que @yydl donne une raison convaincante sur la raison pour laquelle la newInstance est meilleure :

Si Android décide de recréer le constructeur sans argument de votre fragment. Donc surcharger le constructeur n'est pas une solution.

il est encore tout à fait possible d'utiliser un Constructeur . Pour comprendre pourquoi, il faut d'abord voir pourquoi la solution de contournement ci-dessus est utilisée par Android.

Avant qu'un fragment puisse être utilisé, une instance est nécessaire. Android appelle YourFragment() (le pas d'arguments constructor) pour construire une instance du fragment. Ici, tout constructeur surchargé que vous écrivez sera ignoré, car Android ne peut pas savoir lequel utiliser.

Au cours de la vie d'une activité, le fragment est créé comme ci-dessus et détruit plusieurs fois par Android. Cela signifie que si vous placez des données dans l'objet fragment lui-même, elles seront perdues lorsque le fragment sera détruit.

Pour contourner ce problème, Android vous demande de stocker les données à l'aide d'un fichier de type Bundle (appel setArguments() ), qui peut ensuite être consulté à partir de YourFragment . Argumentaire bundle sont protégés par Android, et sont donc garantis d'être persistant .

Une façon de définir ce regroupement est d'utiliser un fichier statique de type newInstance méthode :

public static YourFragment newInstance (int data) {
    YourFragment yf = new YourFragment()
    /* See this code gets executed immediately on your object construction */
    Bundle args = new Bundle();
    args.putInt("data", data);
    yf.setArguments(args);
    return yf;
}

Cependant, un constructeur :

public YourFragment(int data) {
    Bundle args = new Bundle();
    args.putInt("data", data);
    setArguments(args);
}

peut faire exactement la même chose que le newInstance méthode.

Naturellement, cela échouerait, et c'est l'une des raisons pour lesquelles Android veut que vous utilisiez l'option newInstance méthode :

public YourFragment(int data) {
    this.data = data; // Don't do this
}

Pour plus d'explications, voici la classe des fragments d'Android :

/**
 * Supply the construction arguments for this fragment.  This can only
 * be called before the fragment has been attached to its activity; that
 * is, you should call it immediately after constructing the fragment.  The
 * arguments supplied here will be retained across fragment destroy and
 * creation.
 */
public void setArguments(Bundle args) {
    if (mIndex >= 0) {
        throw new IllegalStateException("Fragment already active");
    }
    mArguments = args;
}

Notez qu'Android demande que les arguments soient définis seulement lors de la construction, et garantit que ceux-ci seront conservés.

EDIT : Comme l'a souligné dans les commentaires @JHH, si vous fournissez un constructeur personnalisé qui nécessite certains arguments, alors Java ne fournira pas à votre fragment un pas d'argument Constructeur par défaut. Il faudrait donc que vous définissiez un pas d'argument ce qui représente du code que vous pourriez éviter avec l'option newInstance méthode d'usine.

EDIT : Android ne permet plus d'utiliser un constructeur surchargé pour les fragments. Vous devez utiliser le newInstance méthode.

32voto

Rafols Points 301

Quelques kotlin code :

companion object {
    fun newInstance(first: String, second: String) : SampleFragment {
        return SampleFragment().apply {
            arguments = Bundle().apply {
                putString("firstString", first)
                putString("secondString", second)
            }
        }
    }
}

Et vous pouvez obtenir des arguments avec ça :

val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X