Question Meilleure pratique pour instancier un nouveau fragment Android [fermé]


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

Fragment newFragment = new MyFragment();

et

Fragment newFragment = MyFragment.newInstance();

La deuxième option utilise une méthode statique newInstance() et 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 pourrais surcharger la méthode newInstance () pour donner de la flexibilité lors de la création de nouvelles instances d'un fragment - mais je pourrais aussi le faire en créant un constructeur surchargé pour le fragment.

Ai-je manqué quelque chose?

Quels sont les avantages d'une approche par rapport à l'autre? Ou est-ce juste une bonne pratique?


580
2018-02-12 00:42


origine


Réponses:


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

Cela étant dit, la façon de transmettre des éléments à votre Fragment afin qu'ils soient disponibles après qu'un Fragment ait été recréé par Android est de transmettre un paquet au 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 tard dans le fragment onCreate() vous pouvez accéder à cet entier en utilisant:

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

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

Notez également: setArguments ne peut être appelé que lorsque le fragment est associé à 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


963
2018-02-12 00:57



Le seul avantage à 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 empaqueté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 arguments attend pour travailler fidèlement (bien que vous devriez être capable de gérer des cas si aucun argument n'est regroupé dans l'instance de fragment).

Donc, ma prise est que l'utilisation d'un statique newInstance() Instancier un fragment est une bonne pratique.


90
2018-02-12 15:43



Il y a aussi une autre façon:

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

54
2018-03-09 14:26



Alors que @yydl donne une raison convaincante sur pourquoi le newInstance la méthode est meilleure:

Si Android décide de recréer votre fragment plus tard, il va appeler   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 voir pourquoi c'est, nous devons d'abord voir pourquoi la solution ci-dessus est utilisée par Android.

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

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

Pour contourner le problème, Android vous demande de stocker les données en utilisant un Bundle (appel setArguments()), qui peut ensuite être consulté à partir de YourFragment. Argument bundles sont protégés par Android, et sont donc garantis d'être persistant.

Une façon de définir ce bundle consiste à utiliser un static 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 newInstance méthode:

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

Comme explication supplémentaire, voici la classe Fragment 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 à la construction, et garantit que ceux-ci seront conservés.

MODIFIER: Comme indiqué dans les commentaires de @JHH, si vous fournissez un constructeur personnalisé qui nécessite des arguments, Java ne fournira pas votre fragment sans arg constructeur par défaut. Donc, cela vous obligerait à définir un sans arg constructeur, qui est le code que vous pourriez éviter avec le newInstance méthode d'usine.

MODIFIER: Android n'autorise plus l'utilisation d'un constructeur surchargé pour les fragments. Vous devez utiliser le newInstance méthode.


32
2018-06-16 12:39



je être en désaccord avec yydi répondre en disant:

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

Je pense que c'est une solution et une bonne, c'est exactement la raison pour laquelle elle a été développée par le langage Java.

C'est vrai que le système Android peut détruire et recréer votre Fragment. Donc vous pouvez faire ceci:

public MyFragment() {
//  An empty constructor for Android System to use, otherwise exception may occur.
}

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

Cela vous permettra de tirer someInt de getArguments() dernier, même si le Fragment été recréé par le système. C'est une solution plus élégante que static constructeur.

À mon avis static Les constructeurs sont inutiles et ne devraient pas être utilisés. En outre, ils vous limiteront si à l'avenir vous souhaitez étendre cette Fragment et ajouter plus de fonctionnalités au constructeur. Avec static constructeur vous ne pouvez pas faire cela.

Mettre à jour:

Android a ajouté une inspection qui marque tous les constructeurs autres que ceux par défaut avec une erreur.
Je recommande de le désactiver, pour les raisons mentionnées ci-dessus.


14
2017-09-23 12:14



Certains 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 ceci:

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

8
2018-01-17 07:53



La meilleure pratique pour l'instance de fragments avec des arguments dans Android est d'avoir une méthode d'usine statique dans votre fragment.

public static MyFragment newInstance(String name, int age) {
    Bundle bundle = new Bundle();
    bundle.putString("name", name);
    bundle.putInt("age", age);

    MyFragment fragment = new MyFragment();
    fragment.setArguments(bundle);

    return fragment;
}

Vous devriez éviter de définir vos champs avec l'instance d'un fragment. Parce que chaque fois que le système Android recréera votre fragment, s'il estime que le système a besoin de plus de mémoire, il recréera votre fragment en utilisant un constructeur sans arguments.

Vous pouvez trouver plus d'informations sur meilleure pratique pour instancier des fragments avec des arguments ici.


3
2017-11-01 18:03



Depuis les questions sur les meilleures pratiques, j'ajouterais, c'est très souvent une bonne idée d'utiliser une approche hybride pour créer des fragments lorsque vous travaillez avec certains services web REST

Nous ne pouvons pas passer d'objets complexes, par exemple un modèle d'utilisateur, en cas d'affichage d'un fragment d'utilisateur

Mais ce que nous pouvons faire, c'est de vérifier onCreate cet utilisateur! = null et si non - alors le ramener de la couche de données, sinon - utiliser existant.

De cette façon, nous gagnons à la fois la possibilité de recréer par userId en cas de fragmentation par Android et le snappiness pour les actions de l'utilisateur, ainsi que la possibilité de créer des fragments en maintenant l'objet lui-même ou seulement son identifiant

Quelque chose aime ça:

public class UserFragment extends Fragment {
    public final static String USER_ID="user_id";
    private User user;
    private long userId;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userId = getArguments().getLong(USER_ID);
        if(user==null){
            //
            // Recreating here user from user id(i.e requesting from your data model,
            // which could be services, direct request to rest, or data layer sitting
            // on application model
            //
             user = bringUser();
        }
    }

    public static UserFragment newInstance(User user, long user_id){
        UserFragment userFragment = new UserFragment();
        Bundle args = new Bundle();
        args.putLong(USER_ID,user_id);
        if(user!=null){
            userFragment.user=user;
        }
        userFragment.setArguments(args);
        return userFragment;

    }

    public static UserFragment newInstance(long user_id){
        return newInstance(null,user_id);
    }

    public static UserFragment newInstance(User user){
        return newInstance(user,user.id);
    }
}

2
2018-03-04 13:29



La meilleure façon d'instancier le fragment est l'utilisation par défaut Fragment.instantiate méthode ou créer une méthode usine pour instancier le fragment
Attention: créez toujours un constructeur vide dans un autre fragment tandis que la restauration de la mémoire fragmentée lancera une exception d'exécution.


0
2018-06-30 09:34