menus - menu overflow android studio
Las mejores prácticas para crear una instancia de un nuevo fragmento de Android (11)
He visto dos prácticas generales para crear una instancia de un nuevo fragmento en una aplicación:
Fragment newFragment = new MyFragment();
y
Fragment newFragment = MyFragment.newInstance();
La segunda opción hace uso de un método estático newInstance()
y generalmente 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 sólo una buena práctica?
Algún código kotlin :
companion object {
fun newInstance(first: String, second: String) : SampleFragment {
return SampleFragment().apply {
arguments = Bundle().apply {
putString("firstString", first)
putString("secondString", second)
}
}
}
}
Y puedes conseguir discusiones con esto:
val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}
Creo que tengo una solución mucho más simple 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;
}
Desde las preguntas sobre las mejores prácticas, agregaría, que a menudo es una buena idea utilizar 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, en el caso de mostrar un fragmento de usuario
Pero lo que podemos hacer es registrar onCreate
ese usuario! = onCreate
y, si no, llevarlo desde la capa de datos, de lo contrario, usar el existente.
De esta manera, obtendremos tanto la capacidad de recrear por userId en el caso de la recreación de fragmentos por parte de Android como la rapidez de las acciones de los usuarios, así como la capacidad de crear fragmentos manteniendo el objeto en sí 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);
}
}
El único beneficio en el uso de newInstance()
que veo es el siguiente:
Tendrá un solo lugar donde todos los argumentos utilizados por el fragmento podrían agruparse y no tendrá que escribir el código a continuación cada vez que cree una instancia de un fragmento.
Bundle args = new Bundle(); args.putInt("someInt", someInt); args.putString("someString", someString); // Put any other arguments myFragment.setArguments(args);
Es una buena manera de decirle a otras clases qué argumentos espera que funcionen fielmente (aunque debería poder manejar los casos si no hay argumentos agrupados en la instancia del fragmento).
Por lo tanto, mi opinión es que usar una newInstance()
estática para instanciar un fragmento es una buena práctica.
La mejor manera de crear una instancia del fragmento es usar el método Fragment.instantiate predeterminado o crear un método de fábrica para crear una instancia del fragmento.
Precaución: siempre cree un constructor vacío en el fragmento otro mientras se restaura la memoria de fragmentos se generará una excepción en tiempo de ejecución.
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 siempre que el sistema Android vuelva a crear su fragmento, si siente que el sistema necesita más memoria, entonces recreará su fragmento usando un constructor sin argumentos.
Puede encontrar más información sobre las mejores prácticas para instanciar fragmentos con argumentos aquí.
Mientras que @yydl da una razón convincente de por qué el método newInstance
es mejor:
Si Android decide recrear tu Fragmento más tarde, llamará al 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é Android utiliza la solución anterior.
Antes de poder utilizar un fragmento, se necesita una instancia. Android llama a YourFragment()
(el constructor sin argumentos ) 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 indica arriba y se destruye varias veces con Android. Esto significa que si coloca datos en el objeto del fragmento en sí, se perderá una vez que se destruya el fragmento.
Para solucionar el problema, Android le pide que almacene datos utilizando un Bundle
(que llama a setArguments()
), al que se puede acceder desde YourFragment
. Los bundle
argumentos están protegidos por Android y, por lo tanto, se garantiza que sean persistentes .
Una forma de configurar este paquete es mediante el uso de un método newInstance
estático:
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 el método newInstance
.
Naturalmente, esto fallaría, y es una de las razones por las que Android quiere que uses el método newInstance
:
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 solicita que los argumentos se establezcan solo en la construcción y garantiza que estos se mantendrán.
EDITAR : Como se señala en los comentarios de @JHH, si está proporcionando un constructor personalizado que requiere algunos argumentos, entonces Java no proporcionará su fragmento con un constructor predeterminado sin argumento. Por lo tanto, esto requerirá que defina un constructor sin argumentos , que es un código que podría evitar con el método de fábrica newInstance
.
EDITAR : Android ya no permite usar un constructor sobrecargado para fragmentos. Debe utilizar el método newInstance
.
No estoy de acuerdo con la answer yydi diciendo:
Si Android decide recrear tu Fragmento más tarde, llamará al 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 fue desarrollada por el lenguaje central de Java.
Es cierto que el sistema Android puede destruir y recrear tu Fragment
. Así que 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);
}
Le permitirá extraer someInt
de getArguments()
, incluso si el Fragment
ha sido recreado por el sistema. Esta es una solución más elegante que static
constructor static
.
En mi opinión, static
constructores static
son inútiles y no deben utilizarse. También le limitarán si en el futuro desea extender este Fragment
y agregar más funcionalidad al constructor. Con static
constructor static
no puedes hacer esto.
Actualizar:
Android agregó inspección que marca todos los constructores no predeterminados con un error.
Recomiendo deshabilitarlo, por las razones antes mencionadas.
Si Android decide recrear tu Fragmento más tarde, llamará al constructor sin argumentos de tu fragmento. Así que sobrecargar el constructor no es una solución.
Dicho esto, la manera de pasar cosas a tu Fragmento para que estén disponibles después de que un Fragmento sea recreado por Android es pasar un paquete al método setArguments
.
Entonces, por ejemplo, si quisiéramos pasar un número 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 más adelante en el fragmento onCreate()
puede acceder a ese entero utilizando:
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
También hay otra manera:
Fragment.instantiate(context, MyFragment.class.getName(), myBundle)
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");
}
...
}
}