java - style - fragments android tutorial
Variables de miembros frente a setArguments en Fragments (4)
Encontré que se trata de un tema ALTAMENTE confuso (uno de los tantos que ensucian el panorama de Android).
setArguments()
es una solución para la muy poco útil necesidad de Android de tener un constructor sin parámetros disponible para los Fragmentos.
Mi confusión vino en oleadas. En primer lugar, los métodos que anula de forma natural en su Fragment
(p. onCreate
, onCreateView
, onCreateView
) reciben un parámetro Bundle
que representa el savedInstanceState
de su Fragment
. Este estado de instancia aparentemente no tiene NADA que ver con los valores que almacena a través de setArguments()
y recupera a través de getArguments()
. Ambos usan un Bundle
, es probable que se acceda a ambos Bundles
dentro del mismo método reemplazado, ninguno tiene nada que ver el uno con el otro.
En segundo lugar, no está claro cómo Android usa setArguments()
. Android llama a tu constructor sin parámetros para reconstruir tu Fragment
on rotate, pero aparentemente TAMBIÉN setArguments()
método que setArguments()
llamado a setArguments()
cuando se construyó el Fragment
.
Huh ???
Increíble, pero cierto. Todo esto crea Bundles
with setArguments()
locura existe para compensar la necesidad de un constructor de Fragment
parámetros.
En resumen, estoy usando el método newInstance
estático para crear mi Fragment
.
public MyFragment() {
//satisfy Android
}
public static MyFragment newInstance(long record_id) {
Log.d("MyFragment", "Putting " + record_id + " into newInstance");
MyFragment f = new MyFragment();
Bundle args = new Bundle();
args.putLong("record_id", record_id);
f.setArguments(args);
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/**
* Perform an immediate check of arguments,
* which ARE NOT the same as the bundle used
* for saved instance state.
*/
Bundle args = getArguments();
if(args != null) {
record_id = args.getLong("record_id");
Log.d("MyFragment", "found record_id of " + String.valueOf(record_id));
}
if(savedInstanceState != null) {
//now do something with savedInstanceState
}
}
Me di cuenta de que en la referencia de Android para Fragments (en particular, DialogFragment ) hacen un par de cosas diferentes de lo que esperaba:
1). Utilice public static foo newInstance()
método public static foo newInstance()
lugar de un constructor.
2). Pase los valores a onCreateDialog utilizando setArguments en lugar de variables miembro.
He leído que newInstance parece ser preferible cuando se usa la reflexión. Sin embargo, realmente no entiendo por qué están pasando parámetros a través de un paquete. Sin embargo, hubiera usado variables de miembro sería más seguro (no usar una cadena para recuperar de un mapa) y tendría menos de una sobrecarga.
¿Alguna idea?
Solo quiero agregar una desventaja más a los argumentos es que tienes que crear fragmentos dinámicamente. Como argumentos, no funciona muy bien si crea desde el xml. Y realmente odio eso.
Soy bastante nuevo en la programación de Android, pero esta es mi comprensión actual del problema:
El constructor para Fragmentos no puede tener ningún parámetro. Cuando tu actividad está en pausa, tu Fragmento puede ser liberado. Antes de reanudar su actividad, el sistema crea una nueva versión de su Fragmento llamando al constructor. Si se utiliza un constructor no predeterminado, ¿cómo se supone que Android sabrá cuáles son los tipos y valores para los argumentos de tu constructor de Fragments?
No creo que ese paquete sea lanzado. El paquete se mantiene alrededor precisamente para que pueda volver a pasar a su Fragmento después de que se haya recreado con el constructor predeterminado.
Philipp Reichart eludió esto en su publicación (en realidad más que eludido).
También me encontré con esto y encontré algunas ventajas al usar los argumentos Bundle
sobre los campos de instancia:
Si está en un
Bundle
el sistema Android lo sabe y puede crear y destruir tuFragment
(utilizando el constructor obligatorio y sin parámetros obligatorio y los métodos usuales del ciclo de vida), y simplemente vuelve a pasar el paquete de argumentos. De esta manera, no se pierden argumentos en una juerga de poca memoria o la orientación eventual cambia (esto a menudo me golpea en la primera implementación en un dispositivo real después del desarrollo en el emulador menos giratorio).Puede pasar el
Bundle
de extras de unaActivity
tal como está a unFragment
incrustado en el diseño; Por ejemplo, a menudo uso esto cuando tengo unaActivity
que muestra unFragment
"pantalla completa" y necesito alguna identificación (o URI delContentProvider
) para saber qué mostrar / hacer. A veces incluso agrego más cosas a unBundle
(o una copia) antes de pasarlo, por ejemplo@Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { // not a re-creation final Bundle args = new Bundle(getIntent().getExtras()); args.putInt(CoverImageFragment.BACKGROUND_RESOURCE, android.R.color.black); final Fragment fragment = CoverImageFragment.newInstance(args); getSupportFragmentManager() .beginTransaction() .add(android.R.id.content, fragment) .commit(); } }
Mantiene la forma de desarrollar un
Fragment
cercano al de unaActivity
, es decir,Bundle
como "parámetros de entrada, sin excepciones".
En cuanto a los inconvenientes que mencionaste:
Creo que la sobrecarga es mínima porque lo más probable es que no consulte el
Bundle
en un circuito cerrado, por lo que sacar los datos de su argumento una vez enonCreate()
, enonViewCreate()
, etc. no es tan malo.Para la seguridad del tipo,
Bundle
tiene todos los diferentes métodosgetXXXX()
, e incluso sobrecargas para proporcionar un valor predeterminado si falta algo / opcional :)
En cuanto a los métodos newInstance()
, pienso en ellos como una manera fácil de encapsular las llamadas new
y setArguments()
para mi Fragment
; A veces proporciono un MyFragment newInstance(String singleIdOfWhatToDisplay)
adicional MyFragment newInstance(String singleIdOfWhatToDisplay)
que crea tanto el Bundle
como el Fragment
de una vez y devuelve una instancia de Fragment
lista para Fragment
.