ÚLTIMA INFORMACIÓN:
He reducido mi problema a ser un problema con fragmentManager que conserva instancias de fragmentos antiguos y mi visor no está sincronizado con mi FragmentManager. Ver este problema ... http://code.google.com/p/android/issues/detail?id=19211#makechanges . Todavía no tengo ni idea de cómo solucionar esto. Alguna sugerencia...
He intentado depurar esto durante mucho tiempo y cualquier ayuda sería muy apreciada. Estoy usando un FragmentPagerAdapter que acepta una lista de fragmentos como este:
List<Fragment> fragments = new Vector<Fragment>();
fragments.add(Fragment.instantiate(this, Fragment1.class.getName()));
...
new PagerAdapter(getSupportFragmentManager(), fragments);
La implementación es estándar. Estoy usando ActionBarSherlock y la biblioteca de computabilidad v4 para Fragments.
Mi problema es que después de salir de la aplicación y abrir varias otras aplicaciones y regresar, los fragmentos pierden su referencia a FragmentActivity (es decir getActivity() == null
). No puedo entender por qué está sucediendo esto. Traté de configurar manualmente setRetainInstance(true);
pero esto no ayuda. Supuse que esto sucede cuando se destruye mi FragmentActivity, sin embargo, esto aún sucede si abro la aplicación antes de recibir el mensaje de registro. ¿Hay alguna idea?
@Override
protected void onDestroy(){
Log.w(TAG, "DESTROYDESTROYDESTROYDESTROYDESTROYDESTROYDESTROY");
super.onDestroy();
}
El adaptador:
public class PagerAdapter extends FragmentPagerAdapter {
private List<Fragment> fragments;
public PagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return this.fragments.get(position);
}
@Override
public int getCount() {
return this.fragments.size();
}
}
Uno de mis Fragmentos se quitó, pero me encontré con todo eso está quitado y todavía no funciona ...
public class MyFragment extends Fragment implements MyFragmentInterface, OnScrollListener {
...
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
handler = new Handler();
setHasOptionsMenu(true);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
Log.w(TAG,"ATTACHATTACHATTACHATTACHATTACH");
context = activity;
if(context== null){
Log.e("IS NULL", "NULLNULLNULLNULLNULLNULLNULLNULLNULLNULLNULL");
}else{
Log.d("IS NOT NULL", "NOTNOTNOTNOTNOTNOTNOTNOT");
}
}
@Override
public void onActivityCreated(Bundle savedState) {
super.onActivityCreated(savedState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.my_fragment,container, false);
return v;
}
@Override
public void onResume(){
super.onResume();
}
private void callService(){
// do not call another service is already running
if(startLoad || !canSet) return;
// set flag
startLoad = true;
canSet = false;
// show the bottom spinner
addFooter();
Intent intent = new Intent(context, MyService.class);
intent.putExtra(MyService.STATUS_RECEIVER, resultReceiver);
context.startService(intent);
}
private ResultReceiver resultReceiver = new ResultReceiver(null) {
@Override
protected void onReceiveResult(int resultCode, final Bundle resultData) {
boolean isSet = false;
if(resultData!=null)
if(resultData.containsKey(MyService.STATUS_FINISHED_GET)){
if(resultData.getBoolean(MyService.STATUS_FINISHED_GET)){
removeFooter();
startLoad = false;
isSet = true;
}
}
switch(resultCode){
case MyService.STATUS_FINISHED:
stopSpinning();
break;
case SyncService.STATUS_RUNNING:
break;
case SyncService.STATUS_ERROR:
break;
}
}
};
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear();
inflater.inflate(R.menu.activity, menu);
}
@Override
public void onPause(){
super.onPause();
}
public void onScroll(AbsListView arg0, int firstVisible, int visibleCount, int totalCount) {
boolean loadMore = /* maybe add a padding */
firstVisible + visibleCount >= totalCount;
boolean away = firstVisible+ visibleCount <= totalCount - visibleCount;
if(away){
// startLoad can now be set again
canSet = true;
}
if(loadMore)
}
public void onScrollStateChanged(AbsListView arg0, int state) {
switch(state){
case OnScrollListener.SCROLL_STATE_FLING:
adapter.setLoad(false);
lastState = OnScrollListener.SCROLL_STATE_FLING;
break;
case OnScrollListener.SCROLL_STATE_IDLE:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_IDLE;
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
adapter.setLoad(true);
if(lastState == SCROLL_STATE_FLING){
// load the images on screen
}
lastState = OnScrollListener.SCROLL_STATE_TOUCH_SCROLL;
break;
}
}
@Override
public void onDetach(){
super.onDetach();
if(this.adapter!=null)
this.adapter.clearContext();
Log.w(TAG, "DETACHEDDETACHEDDETACHEDDETACHEDDETACHEDDETACHED");
}
public void update(final int id, String name) {
if(name!=null){
getActivity().getSupportActionBar().setTitle(name);
}
}
}
Se llama al método de actualización cuando un usuario interactúa con un fragmento diferente y getActivity devuelve un valor nulo. Aquí está el método al que llama el otro fragmento ...
((MyFragment) pagerAdapter.getItem(1)).update(id, name);
Creo que cuando la aplicación se destruye y luego se crea nuevamente en lugar de simplemente iniciar la aplicación hasta el fragmento predeterminado, la aplicación se inicia y luego el visor navega a la última página conocida. Esto parece extraño, ¿no debería la aplicación cargarse en el fragmento predeterminado?
fuente
Respuestas:
Se encuentra con un problema porque está creando instancias y manteniendo referencias a sus fragmentos fuera de
PagerAdapter.getItem
, y está tratando de usar esas referencias independientemente de ViewPager. Como dice Seraph, tiene garantías de que se ha creado una instancia / agregado de un fragmento en un ViewPager en un momento particular; esto debe considerarse un detalle de implementación. Un ViewPager realiza una carga diferida de sus páginas; de forma predeterminada, solo carga la página actual y la de la izquierda y la derecha.Si coloca su aplicación en segundo plano, los fragmentos que se han agregado al administrador de fragmentos se guardan automáticamente. Incluso si se mata su aplicación, esta información se restaura cuando reinicia su aplicación.
Ahora considere que ha visto algunas páginas, Fragmentos A, B y C. Sabe que se han agregado al administrador de fragmentos. Porque estás usando
FragmentPagerAdapter
y noFragmentStatePagerAdapter
, estos fragmentos se agregarán (pero potencialmente se separarán) cuando se desplace a otras páginas.Considere que luego pone en segundo plano su aplicación y luego se mata. Cuando regrese, Android recordará que solía tener Fragmentos A, B y C en el administrador de fragmentos, por lo que los recrea para usted y luego los agrega. Sin embargo, los que se agregan al administrador de fragmentos ahora NO son los que tiene en su lista de fragmentos en su Actividad.
El FragmentPagerAdapter no intentará llamar
getPosition
si ya hay un fragmento agregado para esa posición de página en particular. De hecho, dado que el fragmento recreado por Android nunca se eliminará, no tiene esperanzas de reemplazarlo con una llamada agetPosition
. Controlarlo también es bastante difícil obtener una referencia porque se agregó con una etiqueta que usted desconoce. Esto es por diseño; se le desaconseja jugar con los fragmentos que administra el buscapersonas de vista. Debe realizar todas sus acciones dentro de un fragmento, comunicarse con la actividad y solicitar cambiar a una página en particular, si es necesario.Ahora, volvamos a su problema con la actividad faltante. Llamar
pagerAdapter.getItem(1)).update(id, name)
después de que todo esto haya sucedido le devuelve el fragmento en su lista, que aún no se ha agregado al administrador de fragmentos , por lo que no tendrá una referencia de actividad. Sugeriría que su método de actualización debería modificar alguna estructura de datos compartidos (posiblemente administrada por la actividad), y luego, cuando se mueva a una página en particular, puede dibujarse a sí mismo en función de estos datos actualizados.fuente
instantiateItem
y debería hacerlo dentroonCreate
de su actividad. vea los detalles aquí: stackoverflow.com/questions/14035090/…Encontré una solución simple que funcionó para mí.
Haga que su adaptador de fragmentos extienda FragmentStatePagerAdapter en lugar de FragmentPagerAdapter y anule el método onSave para devolver nulo
Esto evita que Android vuelva a crear fragmentos
Un día después encontré otra y mejor solución.
Solicite
setRetainInstance(true)
todos sus fragmentos y guarde las referencias a ellos en algún lugar. Hice eso en una variable estática en mi actividad, porque se declara como singleTask y los fragmentos pueden permanecer iguales todo el tiempo.De esta manera, Android no recrea fragmentos sino que usa las mismas instancias.
fuente
setRetainInstance(true)
conFragmentPagerAdapter
. Todo funciona bien. Pero cuando giro el dispositivo, el adaptador todavía tiene los fragmentos, pero los fragmentos no se muestran. Los métodos de ciclo de vida de los fragmentos tampoco se llaman. ¿Alguien puede ayudar?Resolví este problema accediendo a mis fragmentos directamente a través del FragmentManager en lugar de a través del FragmentPagerAdapter como tal. Primero necesito averiguar la etiqueta del fragmento generado automáticamente por FragmentPagerAdapter ...
Luego simplemente obtengo una referencia a ese fragmento y hago lo que necesito así ...
Dentro de mis fragmentos configuré el
setRetainInstance(false);
para poder agregar manualmente valores al paquete SavedInstanceState.y luego en OnCreate agarro esa clave y restauro el estado del fragmento según sea necesario. Una solución fácil que fue difícil (al menos para mí) de encontrar.
fuente
FragmentByTag
en ViewPager.Solución probada de trabajo global.
getSupportFragmentManager()
mantiene la referencia nula algunas veces y View pager no crea una nueva ya que encuentra una referencia al mismo fragmento. Entonces, para superar este uso segetChildFragmentManager()
resuelve el problema de manera simple.No hagas esto:
new PagerAdapter(getSupportFragmentManager(), fragments);
Hacer esto:
new PagerAdapter(getChildFragmentManager() , fragments);
fuente
FragmentStatePagerAdapter(activity!!.supportFragmentManager)
ser más fácil de verFragmentStatePagerAdapter(childFragmentManager)
:)No intente interactuar entre fragmentos en ViewPager. No puede garantizar que exista otro fragmento adjunto o incluso. En lugar de cambiar el título de la barra de acción por un fragmento, puede hacerlo desde su actividad. Utilice el patrón de interfaz estándar para esto:
fuente
Puede eliminar los fragmentos cuando destruya el visor, en mi caso, los eliminé
onDestroyView()
de mi fragmento:fuente
ViewPager
también se base enchildFragmentManager
adaptador (nofragmentManager
). También funciona otra variante: no usaronDestroyView
, pero eliminar fragmentos secundarios antes de laViewPager
creación del adaptador.Después de unas horas de buscar un problema similar, creo que tengo otra solución. Este al menos me funcionó y solo tengo que cambiar un par de líneas.
Este es el problema que tuve, tengo una actividad con un paginador de vista que usa un FragmentStatePagerAdapter con dos Fragmentos. Todo funciona bien hasta que fuerzo la actividad a ser destruida (opciones de desarrollador) o giro la pantalla. Mantengo una referencia a los dos fragmentos después de que se crean dentro del método getItem.
En ese punto, la actividad se creará nuevamente y todo funciona bien en este punto, pero he perdido la referencia a mis fragmetns ya que getItem no se vuelve a llamar.
Así es como solucioné ese problema, dentro del FragmentStatePagerAdapter:
No volverá a recibir una llamada en getItem si el adaptador ya tiene una referencia internamente, y no debería cambiar eso. En su lugar, puede obtener el fragmento que se está utilizando mirando este otro método instantiateItem () que se llamará para cada uno de sus fragmentos.
Espero que ayude a alguien.
fuente
Dado que FragmentManager se encargará de restaurar sus Fragmentos por usted tan pronto como se llame al método onResume (), hago que el fragmento llame a la actividad y se agregue a una lista. En mi caso, estoy almacenando todo esto en mi implementación de PagerAdapter. Cada fragmento conoce su posición porque se agrega a los argumentos del fragmento en la creación. Ahora, cada vez que necesito manipular un fragmento en un índice específico, todo lo que tengo que hacer es usar la lista de mi adaptador.
El siguiente es un ejemplo de un adaptador para un ViewPager personalizado que hará crecer el fragmento a medida que se mueve hacia el foco y lo reduce a medida que se desenfoca. Además de las clases Adaptador y Fragmento que tengo aquí, todo lo que necesita es que la actividad principal pueda hacer referencia a la variable del adaptador y ya está.
Adaptador
Fragmento
fuente
Mi solución: configuré casi todas las vistas como
static
. Ahora mi aplicación interactúa perfectamente. Ser capaz de llamar a los métodos estáticos desde cualquier lugar tal vez no sea un buen estilo, pero ¿por qué jugar con un código que no funciona? Leí muchas preguntas y sus respuestas aquí en SO y ninguna solución trajo éxito (para mí).Sé que puede perder la memoria y desperdiciar el montón, y mi código no se ajustará a otros proyectos, pero no me asusta esto: probé la aplicación en diferentes dispositivos y condiciones, sin ningún problema, el Android La plataforma parece poder manejar esto. La interfaz de usuario se actualiza cada segundo e incluso en un dispositivo S2 ICS (4.0.3) la aplicación puede manejar miles de marcadores geográficos.
fuente
Enfrenté el mismo problema, pero mi ViewPager estaba dentro de un TopFragment que creó y configuró un adaptador usando
setAdapter(new FragmentPagerAdapter(getChildFragmentManager()))
.onAttachFragment(Fragment childFragment)
Solucioné este problema anulando en el TopFragment de esta manera:Como ya se sabe (vea las respuestas arriba), cuando childFragmentManager se recrea a sí mismo, también crea los fragmentos que estaban dentro de viewPager.
¡Lo importante es que después de eso, llama a onAttachFragment y ahora tenemos una referencia al nuevo fragmento recreado!
Espero que esto ayude a cualquiera a obtener esta vieja Q como yo :)
fuente
Resolví el problema guardando los fragmentos en SparceArray:
fuente
Solo para que sepas...
Además de la letanía de problemas con estas clases, hay un error bastante interesante que vale la pena compartir.
Estoy usando un ViewPager para navegar por un árbol de elementos (seleccione un elemento y el paginador de vista anima el desplazamiento hacia la derecha, y aparece la siguiente rama, navegue hacia atrás y ViewPager se desplaza en la dirección opuesta para regresar al nodo anterior) .
El problema surge cuando empujo y saco fragmentos del final del FragmentStatePagerAdapter. Es lo suficientemente inteligente como para notar que los elementos cambian y lo suficientemente inteligente como para crear y reemplazar un fragmento cuando el elemento ha cambiado. Pero no lo suficientemente inteligente como para descartar el estado del fragmento, o lo suficientemente inteligente como para recortar los estados del fragmento guardados internamente cuando cambia el tamaño del adaptador. Entonces, cuando hace estallar un elemento y coloca uno nuevo al final, el fragmento del elemento nuevo obtiene el estado guardado del fragmento del elemento anterior, lo que causó un caos absoluto en mi código. Mis fragmentos contienen datos que pueden requerir mucho trabajo para recuperarlos de Internet, por lo que no guardar el estado realmente no era una opción.
No tengo una solución limpia. Usé algo como esto:
Una solución imperfecta porque la nueva instancia de fragmento aún obtiene un paquete de estado guardado, pero al menos no contiene datos obsoletos.
fuente
Dado que la gente no tiende a leer comentarios, aquí hay una respuesta que en su mayoría duplica lo que escribí aquí :
la causa raíz del problema es el hecho de que el sistema Android no llama
getItem
para obtener fragmentos que realmente se muestran, peroinstantiateItem
. Este método primero intenta buscar y reutilizar una instancia de fragmento para una pestaña determinada enFragmentManager
. Solo si esta búsqueda falla (lo que ocurre solo la primera vez cuandoFragmentManager
se crea nuevamente),getItem
se llama. Es por razones obvias no recrear fragmentos (que pueden ser pesados), por ejemplo, cada vez que un usuario gira su dispositivo.Para resolver esto, en lugar de crear fragmentos
Fragment.instantiate
en su actividad, debe hacerlo conpagerAdapter.instantiateItem
y todas estas llamadas deben estar rodeadas porstartUpdate/finishUpdate
llamadas a métodos que inicien / confirmen la transacción del fragmento, respectivamente.getItem
debería ser el lugar donde realmente se crean los fragmentos utilizando sus respectivos constructores.fuente