Pregunta La mejor práctica para crear instancias de un nuevo Fragmento de Android [cerrado]


He visto dos prácticas generales para crear un nuevo Fragmento en una aplicación:

Fragment newFragment = new MyFragment();

y

Fragment newFragment = MyFragment.newInstance();

La segunda opción utiliza un método estático newInstance() y en general contiene el siguiente método

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

Al principio, pensé que el principal beneficio era el hecho de que podía sobrecargar el método newInstance () para dar flexibilidad al crear nuevas instancias de un Fragmento, pero también podía hacerlo creando un constructor sobrecargado para el Fragmento.

¿Me he perdido algo?

¿Cuáles son los beneficios de un enfoque sobre el otro? ¿O es solo una buena práctica?


580
2018-02-12 00:42


origen


Respuestas:


Si Android decide recrear su Fragmento más tarde, va a llamar al constructor sin argumentos de su fragmento. Así que sobrecargar el constructor no es una solución.

Habiendo dicho eso, la forma de pasar cosas a tu Fragmento para que estén disponibles después de que un Fragmento sea recreado por Android es pasarle un paquete al setArguments método.

Entonces, por ejemplo, si quisiéramos pasar un entero al fragmento, usaríamos algo como:

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

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

    return myFragment;
}

Y luego en el Fragmento onCreate() puedes acceder a ese entero usando:

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

Este paquete estará disponible incluso si el fragmento es de alguna manera recreado por Android.

También tenga en cuenta: setArguments solo se puede llamar antes de que el Fragmento se adjunte a la Actividad.

Este enfoque también está documentado en la referencia del desarrollador de Android: https://developer.android.com/reference/android/app/Fragment.html


963
2018-02-12 00:57



El único beneficio al usar el newInstance() que veo son los siguientes:

  1. Tendrás un solo lugar donde todos los argumentos utilizados por el fragmento podrían agruparse y no tienes que escribir el código a continuación cada vez que instancias un fragmento.

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    args.putString("someString", someString);
    // Put any other arguments
    myFragment.setArguments(args);
    
  2. Es una buena manera de contarle a otras clases qué argumentos espera para trabajar fielmente (aunque deberías ser capaz de manejar casos si no hay paquetes agrupados en la instancia del fragmento).

Por lo tanto, mi opinión es que el uso de una estática newInstance() Instalar un fragmento es una buena práctica.


90
2018-02-12 15:43



También hay otra forma:

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

54
2018-03-09 14:26



Mientras @yydl da una razón convincente de por qué newInstance método es mejor:

Si Android decide recrear su Fragmento más tarde, va a llamar   el constructor sin argumentos de tu fragmento. Así que sobrecargar el   constructor no es una solución.

todavía es bastante posible usar un constructor. Para ver por qué esto es así, primero debemos ver por qué la solución anterior es utilizada por Android.

Antes de que se pueda usar un fragmento, se necesita una instancia. Llamadas de Android YourFragment() (el sin argumentos constructor) para construir una instancia del fragmento. Aquí se ignorará cualquier constructor sobrecargado que escriba, ya que Android no puede saber cuál usar.

Durante la vida de una actividad, el fragmento se crea como se describió anteriormente y se destruye varias veces por Android. Esto significa que si coloca datos en el objeto fragmento, se perderá una vez que se destruya el fragmento.

Para solucionarlo, Android le pide que almacene datos usando un Bundle (vocación setArguments()), que luego se puede acceder desde YourFragment. Argumento bundles están protegidos por Android, y por lo tanto están garantizados para ser persistente.

Una forma de configurar este paquete es mediante el uso de un estático newInstance método:

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;
}

Sin embargo, un constructor:

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

puede hacer exactamente lo mismo que newInstance método.

Naturalmente, esto fallaría, y es una de las razones por las que Android quiere que uses el newInstance método:

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

Como explicación adicional, aquí está la clase de fragmentos de 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;
    }

Tenga en cuenta que Android pide que se establezcan los argumentos solamente en la construcción, y garantiza que estos serán retenidos.

EDITAR: Como se señala en los comentarios de @JHH, si proporciona un constructor personalizado que requiere algunos argumentos, entonces Java no proporcionará su fragmento con un no arg Constructor predeterminado. Entonces esto requeriría que definieras un no arg constructor, que es un código que puede evitar con el newInstance método de fábrica.

EDITAR: Android ya no permite el uso de un constructor sobrecargado para fragmentos. Debes usar el newInstance método.


32
2018-06-16 12:39



yo discrepar con yydi responder diciendo:

Si Android decide recrear su Fragmento más tarde, va a llamar   el constructor sin argumentos de tu fragmento. Así que sobrecargar el   constructor no es una solución.

Creo que es una solución y una buena, esta es exactamente la razón por la que ha sido desarrollada por el lenguaje central de Java.

Es cierto que el sistema Android puede destruir y recrear su Fragment. Entonces puedes hacer esto:

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);
}

Te permitirá tirar someInt de getArguments() este último en adelante, incluso si el Fragment sido recreado por el sistema. Esta es una solución más elegante que static constructor.

En mi opinión static los constructores son inútiles y no deberían usarse. También lo limitarán si en el futuro desea extender este Fragment y agrega más funcionalidad al constructor. Con static constructor no puedes hacer esto.

Actualizar:

Android agregó una inspección que marca todos los constructores no predeterminados con un error.
Recomiendo desactivarlo, por las razones mencionadas anteriormente.


14
2017-09-23 12:14



Algunos kotlin código:

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

Y puedes obtener argumentos con esto:

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 mejor práctica para instanciar fragmentos con argumentos en Android es tener un método de fábrica estático en tu fragmento.

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;
}

Debe evitar establecer sus campos con la instancia de un fragmento. Porque cada vez que el sistema Android recrea su fragmento, si siente que el sistema necesita más memoria, entonces recreará su fragmento usando el constructor sin argumentos.

Puedes encontrar más información sobre la mejor práctica para crear instancias de fragmentos con argumentos aquí.


3
2017-11-01 18:03



Dado que las preguntas sobre las mejores prácticas, yo agregaría, esa muy a menudo buena idea es usar un enfoque híbrido para crear fragmentos cuando se trabaja con algunos servicios web REST.

No podemos pasar objetos complejos, por ejemplo, algún modelo de usuario, para el caso de mostrar fragmento de usuario

Pero lo que podemos hacer es registrarnos onCreate ese usuario! = nulo y si no, entonces tráigalo desde la capa de datos; de lo contrario, use el existente.

De esta forma, obtenemos la capacidad de recrear por usuario Id en caso de recreación de fragmento por Android y la ligereza para las acciones de los usuarios, así como la capacidad de crear fragmentos al retener el objeto o solo su ID

Algo le gusta esto:

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 mejor forma de instanciar el fragmento es usar el valor predeterminado Fragmento.instante método o crear un método de fábrica para crear una instancia del fragmento
Precaución: crea siempre un constructor vacío en un fragmento distinto mientras que la restauración de la memoria de fragmento arrojará una excepción en tiempo de ejecución.


0
2018-06-30 09:34



setArguments() es inútil Solo trae un desastre.

public class MyFragment extends Fragment {

    public String mTitle;
    public String mInitialTitle;

    public static MyFragment newInstance(String param1) {
        MyFragment f = new MyFragment();
        f.mInitialTitle = param1;
        f.mTitle = param1;
        return f;
    }

    @Override
    public void onSaveInstanceState(Bundle state) {
        state.putString("mInitialTitle", mInitialTitle);
        state.putString("mTitle", mTitle);
        super.onSaveInstanceState(state);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
        if (state != null) {
            mInitialTitle = state.getString("mInitialTitle");
            mTitle = state.getString("mTitle");
        } 
        ...
    }
}

0
2017-12-06 14:33



Creo que tengo una solución mucho más simpeler para esto.

public class MyFragment extends Fragment{

   private String mTitle;
   private List<MyObject> mObjects;

   public static MyFragment newInstance(String title, List<MyObject> objects)
   MyFragment myFrag = new MyFragment();
   myFrag.mTitle = title;
   myFrag.mObjects = objects;
   return myFrag;
   }

-10
2017-12-29 13:30