ViewPager no vuelve a dibujar el contenido, permanece/se pone en blanco

Estamos sufriendo un problema muy extraño con ViewPager aquí. Incrustamos listas en cada página de ViewPager y activamos la notificación de DataSetChanged tanto en el adaptador de lista como en el adaptador de buscapersonas de vista al actualizar los datos de la lista.

Lo que observamos es que, en ocasiones, la página no actualiza su árbol de vistas, es decir, se queda en blanco o, en ocasiones, incluso desaparece al pasar a la página. Al avanzar y retroceder varias veces, el contenido reaparecerá repentinamente. Parece que a Android le falta una actualización de vista aquí. También noté que al depurar con el visor de jerarquía, seleccionar una vista siempre hará que vuelva a aparecer, aparentemente porque el visor de jerarquía obliga a la vista seleccionada a volver a dibujarse.

Sin embargo, no pude hacer que esto funcione mediante programación; invalidar la vista de lista, o incluso el buscapersonas de vista completo, no tuvo ningún efecto.

Esto es con la biblioteca de compatibilidad-v4_r7. También traté de usar la última revisión, ya que pretende solucionar muchos problemas relacionados con la visualización del buscapersonas, pero empeoró aún más las cosas (por ejemplo, los gestos se rompieron para que ya no me permitiera recorrer todas las páginas a veces).

¿Alguien más también tiene estos problemas o tiene una idea de qué podría estar causando esto?

preguntado el 31 de julio de 12 a las 10:07

13 Respuestas

Si ViewPager se encuentra dentro de un Fragmento con un FragmentPagerAdapter, Utilizar getChildFragmentManager() en lugar de getSupportFragmentManager() como el parámetro para inicializar su FragmentPagerAdapter.

mAdapter = new MyFragmentPagerAdapter(getChildFragmentManager());

En lugar de

mAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager());

Respondido el 18 de diciembre de 15 a las 23:12

Buena captura, parece resolver mi problema cuando uso un FragmentPagerAdapter: Tobliug

Gracias - esto funcionó. estaba tratando de forzar getItem() en un FragmentPagerAdapter que estaba anidado en un Fragmento recreado. - kosiara - Bartosz Kosarzycki

wow este trabajo como el encanto. Supongo que el problema se debe a que el visor está inflado en un fragmento: El carisma

En momentos como este, desearía que tuviéramos aplausos como Medium para poder darte más de 1000 aplausos por esto. Buen trabajo, esta debería ser la respuesta aceptada: Codelicioso

obsoleto. Qué debemos hacer ? - novato

Tuve exactamente el mismo problema pero en realidad destruí la vista en destroyItem (pensé). Sin embargo, el problema fue que lo destruí usando viewPager.removeViewAt(index); insted de viewPager.removeView((View) object);

Incorrecto:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeViewAt(position);
}

A la derecha:

@Override
public void destroyItem(ViewGroup viewPager, int position, Object object) {
    viewPager.removeView((View) object);
}

Respondido 24 Oct 12, 09:10

Gracias por la pista. He estado investigando el tema durante bastante tiempo. - Moritz

Esto funcionó para mí, pero ¿sabes por qué el primero es un problema? - HannahMitt

@HannahMitt removeViewAt eliminará cualquier vista que se encuentre en esa posición particular dentro de la lista actual de niños de ViewPager, y las páginas se pueden agregar a ViewPager en cualquier orden (por lo que la página 0 puede estar en ViewPager en el índice 1 o lo que sea). removeView iterará a través de la lista de elementos secundarios y eliminará exactamente el objeto que se especifica. - usuario153275

No funciona para mí: no se puede enviar la vista al fragmento. objeto aquí es un fragmento. - FRK

visor debe ser estático? - Kanagalingam

Finalmente logramos encontrar una solución. Aparentemente, nuestra implementación sufrió dos problemas:

  1. nuestro adaptador no eliminó la vista en destroyItem().
  2. estábamos almacenando vistas en caché para que tuviéramos que inflar nuestro diseño solo una vez y, dado que no estábamos eliminando la vista en destroyItem(), no lo añadimos instantiateItem() pero simplemente devolviendo la vista en caché correspondiente a la posición actual.

No he buscado demasiado profundamente en el código fuente de la ViewPager - y no es exactamente explícito que tengas que hacer eso - pero los documentos dicen:

destruirItem()
Eliminar una página para la posición dada. El adaptador es responsable de eliminar la vista de su contenedor, aunque solo debe asegurarse de que lo haga cuando regrese de finishUpdate(ViewGroup).

y:

Un PagerAdapter muy simple puede optar por usar las vistas de la página como objetos clave, devolviéndolos desde instantiateItem (ViewGroup, int) después de la creación y agregándolos al ViewGroup principal. Una implementación de destroyItem (ViewGroup, int, Object) coincidente eliminaría la Vista del ViewGroup principal y isViewFromObject (View, Object) podría implementarse como return view == object;.

Así que mi conclusión es que ViewPager se basa en su adaptador subyacente para agregar/eliminar explícitamente sus elementos secundarios en instantiateItem()/destroyItem(). Es decir, si su adaptador es una subclase de PagerAdapter, su subclase debe implementar esta lógica.

Nota al margen: tenga en cuenta este si usas listas dentro ViewPager.

contestado el 23 de mayo de 17 a las 13:05

Tengo el mismo problema pero con FragmentPagerAdapter, que maneja onDestroy para mí. Ninguna de las otras soluciones aquí funciona tampoco. - greg ennis

Lo que también podría ser el problema es que alguien está usando getFragmentManger en lugar de GetChildFragmentManager: Niño

@Boy, ¿qué es GetChildFragmentManager? - fuego en el hoyo

@Boy Wooooow... Estuve tirando de mi cabello durante dos días enteros tratando de averiguar por qué no podía actualizar nada. ¡GRACIAS! (realmente deberían poner un aviso en la documentación de Google para usar el childfragmentmanager, eso no significa que necesite un administrador diferente). - user0721090601

@futtetennista Estoy haciendo lo mismo... frente a este problema... ¿puedes comprobar... stackoverflow.com/questions/61727835/… - Pregunta Q

ViewPager intenta hacer cosas inteligentes en torno a la reutilización de elementos, pero requiere que devuelva nuevas posiciones de elementos cuando las cosas han cambiado. Intente agregar esto a su PagerAdapter:

public int getItemPosition (Object object) { return POSITION_NONE; }

Básicamente le dice a ViewPager que todo ha cambiado (y lo obliga a volver a crear instancias de todo). Eso es lo único que puedo pensar en la parte superior de mi cabeza.

Respondido 31 Jul 12, 10:07

Sí, he leído sobre esta opción en este hilo: stackoverflow.com/questions/7263291/… -- sin embargo, esto parece el enfoque del mazo. ¿Seguramente debe haber una forma más elegante? Me pregunto si está relacionado con el uso de listas como páginas de buscapersonas. - Matías

Es un enfoque un poco mazo, pero se puede trabajar a partir de eso. es decir, devolver un valor correcto del método. - chris banes

@ChrisBanes Como dijiste llamando return POSITION_NONE;, forces it to re-instantiate everything pero en mi caso no quiero volver a instanciar todo, por lo que sería posible eliminar view sin borrar las cosas existentes? Por favor hagamelo saber - ritesh adulkar

eres el sabor de la vida - mask8

Intenté demasiadas soluciones pero inesperadamente viewPager.post() trabajado

 mAdapter = new NewsVPAdapter(getContext(), articles);
    viewPager.post(new Runnable() {
        @Override
        public void run() {
            viewPager.setAdapter(mAdapter);
        }
    });

Respondido el 06 de enero de 19 a las 18:01

Dios mío. ¿Por qué nadie votó esto? Obtuve un comportamiento extraño cuando volví a ingresar un fragmento y el visor que estaba dentro no se representaba solo, ¡y retrasarlo un poco de esta manera resolvió el problema! - Fugogugo

La biblioteca de soporte de Android tiene una actividad de demostración que incluye un ViewPager con un ListView en cada página. Probablemente deberías echarle un vistazo y ver qué hace.

En Eclipse (con Android Dev Tools r20):

  1. Seleccione New > Android Sample Project
  2. Seleccione su nivel de API de destino (sugiero el más nuevo disponible)
  3. Seleccione Support4Demos
  4. Haga clic derecho en el proyecto y seleccione Android Tools > Add Support Library
  5. Ejecute la aplicación y seleccione Fragment y entonces Pager

El código para esto está en src/com.example.android.supportv4.app/FragmentPagerSupport.java. ¡Buena suerte!

Respondido 31 Jul 12, 10:07

gracias - voy a echar un vistazo a eso! Quizás detecte algo en lo que nos estamos equivocando. - Matías

Tuve un problema con los mismos síntomas, pero una causa diferente que resultó ser un error tonto de mi parte. Pensé en agregarlo aquí en caso de que ayude a alguien.

Tenía un ViewPager usando FragmentStatePagerAdapter que solía tener dos fragmentos, pero luego agregué un tercero. Sin embargo, olvidé que el límite predeterminado de páginas fuera de la pantalla es 1, por lo que, cuando cambiaba al nuevo tercer fragmento, el primero se destruía y luego se recreaba después de volver. El problema era que mi actividad se encargaba de notificar a estos fragmentos para que inicializaran su estado de UI. Esto funcionó cuando los ciclos de vida de la actividad y los fragmentos eran los mismos, pero para solucionarlo tuve que cambiar los fragmentos para inicializar su propia interfaz de usuario durante su ciclo de vida de inicio. Al final, también terminé cambiando setOffscreenPageLimit a 2 para que los tres fragmentos se mantuvieran vivos en todo momento (seguro en este caso, ya que no requerían mucha memoria).

Respondido 02 Feb 17, 03:02

Para mí, el problema era volver a la actividad después de que se canceló el proceso de la aplicación. Estoy usando un adaptador de buscapersonas de vista personalizado modificado de las fuentes de Android. El buscapersonas de vista está incrustado directamente en la actividad.

llamar viewPager.setCurrentItem(position, true);

(con animación) después de configurar los datos y notificarDataSetChanged() parece funcionar, pero si el parámetro se establece en falso, no funciona y el fragmento está en blanco. Este es un caso límite que puede ser de ayuda para alguien.

Respondido el 03 de enero de 17 a las 18:01

Para usuarios de Kotlin:

en tus fragmentos; Usar childFragmentManager en lugar de viewPagerAdapter

respondido 28 nov., 20:00

Me encontré con esto y tuve problemas muy similares. incluso yo preguntado en desbordamiento de pila.

para mi, en el padre del padre desde mi punto de vista alguien subclasificado LinearLayout y anuló requestLayout() sin llamar super.requestLayout(). Esto evitó onMeasure y onLayout de ser llamado en mi ViewPager (aunque el visor de jerarquía los llama manualmente). Sin medirse, aparecerán en blanco en ViewPager.

Así que revisa tus vistas contenedoras. Asegúrese de que sean subclases de View y no anulen ciegamente requestLayout o algo similar.

contestado el 23 de mayo de 17 a las 12:05

Tenía el mismo problema, que tiene algo que ver con ListView (porque mi vista vacía se muestra bien si la lista está vacía). acabo de llamar requestLayout() sobre la problemática ListView. ¡Ahora dibuja bien!

Respondido el 19 de diciembre de 12 a las 22:12

Me encontré con este mismo problema al usar ViewPager y FragmentStatePagerAdapter. Intenté usar un controlador con un retraso de 3 segundos para llamar a invalidate() y requestLayout() pero no funcionó. Lo que funcionó fue restablecer el color de fondo de viewPager de la siguiente manera:

MiFragmento.java

    private Handler mHandler;
    private Runnable mBugUpdater;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = new ViewPager(getActivity());
        //...Create your adapter and set it here...

        mHandler = new Handler();
        mBugUpdater = new Runnable(){
            @Override
            public void run() {
                mVp.setBackgroundColor(mItem.getBackgroundColor());
                mHandler = null;
                mBugUpdater = null;
            }           
        };
        mHandler.postDelayed(mBugUpdater,50);

        return rootView;
    }

    @Override
    public void onPause() {
        if(mHandler != null){
            //Remove the callback if it hasn't triggered yet
            mHandler.removeCallbacks(mBugUpdater);
            mHandler = null;
            mBugUpdater = null;
        }
        super.onPause();
     }

Respondido 30 ago 15, 17:08

Tuve un problema similar. Guardo en caché las vistas porque solo necesito 3 vistas en ViewPager. Cuando me deslizo hacia adelante, todo está bien, pero cuando empiezo a deslizarme hacia atrás se produce un error, dice que "mi vista ya tiene un padre". La solución es eliminar manualmente los elementos innecesarios.

@Override
    public Object instantiateItem(ViewGroup container, int position) {
        int localPos = position % SIZE;
        TouchImageView view;
        if (touchImageViews[localPos] != null) {
            view = touchImageViews[localPos];
        } else {
            view = new TouchImageView(container.getContext());
            view.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
            touchImageViews[localPos] = view;
        }
        view.setImageDrawable(mDataModel.getPhoto(position));
        Log.i(IRViewPagerAdpt.class.toString(), "Add view " + view.toString() + " at pos: " + position + " " + localPos);
        if (view.getParent() == null) {
        ((ViewPager) container).addView(view);
    }
        return view;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object view) {
        //      ((ViewPager) container).removeView((View) view);
        Log.i(IRViewPagerAdpt.class.toString(), "remove view " + view.toString() + " at pos: " + position);
    }

..................

private static final int SIZE = 3;
private TouchImageView[] touchImageViews = new TouchImageView[SIZE];

contestado el 24 de mayo de 13 a las 15:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.