studio sola samsung rotación reinicie que porque pantalla movil manejando grados girar gira evitar como celular activity android android-viewpager android-pageradapter fragmentstatepageradapter

android - sola - Destruye el artículo del adaptador de ViewPager después de cambiar la orientación de la pantalla



porque no gira la pantalla de mi celular (1)

Así que estoy teniendo un problema con la destrucción (eliminación) de una página de ViewPager después de que la orientación de la pantalla haya cambiado. Trataré de describir el problema en las siguientes líneas.

Estoy usando FragmentStatePagerAdapter para el adaptador de ViewPager y una pequeña interfaz que describe cómo debería funcionar un buscapersonas sin fin. La idea detrás de esto es que puedes desplazarte hacia la derecha hasta llegar al final de ViewPager . Si puede cargar más resultados de una llamada API, se muestra una página de progreso hasta que lleguen los resultados.

Todo bien hasta aquí, ahora viene el problema. Si durante este proceso de carga, giro la pantalla (esto no afectará a la llamada a la API, que es básicamente una AsyncTask ), cuando la llamada vuelve, la aplicación falla y me da esta excepción:

E/AndroidRuntime(13471): java.lang.IllegalStateException: Fragment ProgressFragment{42b08548} is not currently in the FragmentManager E/AndroidRuntime(13471): at android.support.v4.app.FragmentManagerImpl.saveFragmentInstanceState(FragmentManager.java:573) E/AndroidRuntime(13471): at android.support.v4.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:136) E/AndroidRuntime(13471): at mypackage.OutterFragment$PagedSingleDataAdapter.destroyItem(OutterFragment.java:609)

Después de buscar un poco en el código de la biblioteca, parece que el campo de datos mIndex del fragmento es menor que 0 en este caso, y esto genera esa excepción.

Aquí está el código del adaptador de buscapersonas:

static class PagedSingleDataAdapter extends FragmentStatePagerAdapter implements IEndlessPagerAdapter { private WeakReference<OutterFragment> fragment; private List<DataItem> data; private SparseArray<WeakReference<Fragment>> currentFragments = new SparseArray<WeakReference<Fragment>>(); private ProgressFragment progressElement; private boolean isLoadingData; public PagedSingleDataAdapter(SherlockFragment fragment, List<DataItem> data) { super(fragment.getChildFragmentManager()); this.fragment = new WeakReference<OutterFragment>( (OutterFragment) fragment); this.data = data; } @Override public Object instantiateItem(ViewGroup container, int position) { Object item = super.instantiateItem(container, position); currentFragments.append(position, new WeakReference<Fragment>( (Fragment) item)); return item; } @Override public void destroyItem(ViewGroup container, int position, Object object) { currentFragments.put(position, null); super.destroyItem(container, position, object); } @Override public Fragment getItem(int position) { if (isPositionOfProgressElement(position)) { return getProgessElement(); } WeakReference<Fragment> fragmentRef = currentFragments.get(position); if (fragmentRef == null) { return PageFragment.newInstance(args); // here I''m putting some info // in the args, just deleted // them now, not important } return fragmentRef.get(); } @Override public int getCount() { int size = data.size(); return isLoadingData ? ++size : size; } @Override public int getItemPosition(Object item) { if (item.equals(progressElement) && !isLoadingData) { return PagerAdapter.POSITION_NONE; } return PagerAdapter.POSITION_UNCHANGED; } public void setData(List<DataItem> data) { this.data = data; notifyDataSetChanged(); } @Override public boolean isPositionOfProgressElement(int position) { return isLoadingData && position == data.size(); } @Override public void setLoadingData(boolean isLoadingData) { this.isLoadingData = isLoadingData; } @Override public boolean isLoadingData() { return isLoadingData; } @Override public Fragment getProgessElement() { if (progressElement == null) { progressElement = new ProgressFragment(); } return progressElement; } public static class ProgressFragment extends SherlockFragment { public ProgressFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { TextView progressView = new TextView(container.getContext()); progressView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); progressView.setText(R.string.loading_more_data); LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); progressView.setLayoutParams(params); return progressView; } } }

La devolución de llamada onPageSelected() continuación, que básicamente inicia la llamada de API si es necesario:

@Override public void onPageSelected(int currentPosition) { updatePagerIndicator(currentPosition); activity.invalidateOptionsMenu(); if (requestNextApiPage(currentPosition)) { pagerAdapter.setLoadingData(true); requestNextPageController.requestNextPageOfData(this); }

Ahora, también vale la pena decir lo que hace la llamada API después de entregar los resultados. Aquí está la devolución de llamada:

@Override public boolean onTaskSuccess(Context arg0, List<DataItem> result) { data = result; pagerAdapter.setLoadingData(false); pagerAdapter.setData(result); activity.invalidateOptionsMenu(); return true; }

Bien, ahora porque el método setData() invoca el notifiyDataSetChanged() , esto llamará a getItemPosition() para los fragmentos que están actualmente en la matriz de currentFragments . Por supuesto que para el elemento progress devuelve POSITION_NONE dado que quiero eliminar esta página, por lo que básicamente invoca la devolución de llamada destroyItem() desde PagedSingleDataAdapter . Si no giro la pantalla, todo funciona bien, pero como dije si lo estoy rotando cuando se muestra el elemento de progreso y la llamada a la API aún no ha finalizado, se destroyItem() devolución de llamada destroyItem() después de la actividad se reinicia.

Quizás también debería decir que estoy alojando el ViewPager en otro Fragmento y no en una actividad, por lo que OutterFragment aloja el ViewPager . Estoy ejemplificando el pagerAdapter en la onActivityCreated() de OutterFragment y usando setRetainInstance(true) para que cuando la pantalla gira el pagerAdapter siga siendo el mismo (no se debe cambiar nada, ¿verdad?), Aquí:

if (pagerAdapter == null) { pagerAdapter = new PagedSingleDataAdapter(this, data); } pager.setAdapter(pagerAdapter); if (savedInstanceState == null) { pager.setOnPageChangeListener(this); pager.setCurrentItem(currentPosition); }

Resumiendo ahora, el PROBLEMA es:

Si trato de eliminar el elemento de progreso de ViewPager después de que fue instanciado y la actividad fue destruida y recreada (la orientación de la pantalla cambió) obtengo la excepción anterior (el pagerAdapter sigue siendo el mismo, así que todo dentro de él también permanece igual, referencias etc ... dado que el OutterFragment que aloja el pagerAdapter no se destruye, solo se separa de la actividad y luego se vuelve a adjuntar). Probablemente pase algo con el administrador de fragmentos, pero realmente no sé qué.

Lo que ya he intentado:

  1. Tratando de eliminar mi fragmento de progreso usando otra técnica, es decir, en la devolución de llamada onTaskSuccess() , estaba tratando de eliminar el fragmento del administrador de fragmentos, pero no funcionó.

  2. También traté de ocultar el elemento de progreso en lugar de eliminarlo por completo del administrador de fragmentos. Esto funcionó un 50%, porque la vista ya no estaba allí, pero tenía una página vacía, así que eso no es realmente lo que estoy buscando.

  3. También traté de (re) adjuntar el fragmento de progressFragment al administrador de fragmentos después de que cambie la orientación de la pantalla, esto tampoco funcionó.

  4. También intenté eliminar y luego volver a agregar el fragmento de progreso al administrador de fragmentos después de que se recreara la actividad, pero no funcionó.

  5. destroyItem() llamar manualmente a destroyItem() desde la devolución de llamada onTaskSuccess() (que es realmente, realmente feo) pero no funcionó.

Lo siento chicos por una publicación tan larga, pero estaba tratando de explicar el problema lo mejor que puedo para que ustedes puedan entenderlo.

Cualquier solución, recomendación es muy apreciada.

¡Gracias!

ACTUALIZACIÓN: SOLUCIÓN ENCONTRADA OK, así que esto llevó un tiempo. El problema era que la devolución de llamada destroyItem () se llamaba dos veces en el fragmento de progreso, una vez cuando la orientación de la pantalla cambiaba y luego una vez más después de que finalizara la llamada de API. Es por eso que la excepción. La solución que encontré es la siguiente: Seguir el seguimiento si la llamada api finalizó o no y destruir el fragmento de progreso solo en este caso, código a continuación.

@Override public void destroyItem(ViewGroup container, int position, Object object) { if (object.equals(progressElement) && apiCallFinished == true) { apiCallFinished = false; currentFragments.put(position, currentFragments.get(position + 1)); super.destroyItem(container, position, object); } else if (!(object.equals(progressElement))) { currentFragments.put(position, null); super.destroyItem(container, position, object); } }

y luego este apiCallFinished se establece en falso en el constructor del adaptador y en true en la devolución de llamada onTaskSuccess() . ¡Y realmente funciona!


ACTUALIZACIÓN: SOLUCIÓN ENCONTRADA OK, así que esto llevó un tiempo. El problema era que la devolución de llamada destroyItem () se llamaba dos veces en el fragmento de progreso, una vez cuando la orientación de la pantalla cambiaba y luego una vez más después de que finalizara la llamada de API. Es por eso que la excepción. La solución que encontré es la siguiente: Seguir el seguimiento si la llamada api finalizó o no y destruir el fragmento de progreso solo en este caso, código a continuación.

@Override public void destroyItem(ViewGroup container, int position, Object object) { if (object.equals(progressElement) && apiCallFinished == true) { apiCallFinished = false; currentFragments.put(position, currentFragments.get(position + 1)); super.destroyItem(container, position, object); } else if (!(object.equals(progressElement))) { currentFragments.put(position, null); super.destroyItem(container, position, object); } }

y luego este apiCallFinished se establece en falso en el constructor del adaptador y en true en la devolución de llamada onTaskSuccess (). ¡Y realmente funciona!