Arreglar la animación de un ViewPager Circular

Gol

Construye un ViewPager Circular.

El primer elemento le permite llegar al último elemento y deslizarlo, y viceversa. Debes poder deslizarte en cualquier dirección para siempre.

Ahora esto se ha logrado antes, pero estas preguntas no funcionan para mi implementación. Aquí hay algunos como referencia:

¿Cómo crear viewpager circular?ViewPager como una cola circular / envolturahttps://github.com/antonyt/InfiniteViewPager

Cómo traté de resolver el problema

Usaremos una matriz de tamaño 7 como ejemplo. Los elementos son los siguientes:

[0][1][2][3][4][5][6]

Cuando estás en el elemento 0, los ViewPagers no te dejan deslizar hacia la izquierda. Qué terrible :(. Para evitar esto, agregué 1 elemento al frente y al final.

   [0][1][2][3][4][5][6]      // Original
[0][1][2][3][4][5][6][7][8]   // New mapping

Cuando el ViewPageAdapter solicita el elemento 0 (instantiateItem ()), devolvemos el elemento 7. Cuando el ViewPageAdapter solicita el elemento 8, devolvemos el elemento 1.

Del mismo modo, en OnPageChangeListener en ViewPager, cuando se llama a onPageSelected con 0, establecemos el objeto actual (7), y cuando se llama con 8, establecemos el valor actual (1).

Esto funciona.

El problema

Cuando se desliza hacia la izquierda de 1 a 0, y establecemosCurrentItem (7), se animará completamente a la derecha en 6 pantallas completas. ¡Esto no da la apariencia de un ViewPager circular, da la apariencia de correr al último elemento en la dirección opuesta que el usuario solicitó con su movimiento de deslizar!

Esto es muy, muy discordante.

Como traté de resolver esto

Mi primera inclinación fue desactivar las animaciones suaves (es decir, todas). Es un poco mejor, pero ahora está entrecortado cuando pasas del último elemento al primero y viceversa.

Entonces hice mi propio Scroller.

http://developer.android.com/reference/android/widget/Scroller.html

Lo que encontré fue que siempre hay 1 llamada a startScroll () cuando se mueve entre elementos,excepto Cuando me muevo de 1 a 7 y de 7 a 1.

La primera convocatoria es la animación correcta en dirección y cantidad.

La segunda convocatoria es la animación que mueve todo a la derecha en varias páginas.

Aquí es donde las cosas se pusieron realmente difíciles.

Pensé que la solución era simplemente saltar la segunda animación. Así que lo hice. Lo que pasa es una animación suave de 1 a 7 con 0 contratiempos. ¡Perfecto! Sin embargo, si desliza, o incluso toca la pantalla, de repente se encuentra (sin animación) en el elemento 6. Si ha pasado de 7 a 1, en realidad estará en el elemento 2. No hay ninguna llamada a setCurrentItem (2) o incluso una llamada al OnPageChangeListener que indica que llegó a 2 en cualquier momento.

Pero en realidad no estás en el elemento 2, lo cual es bueno. Todavía estás en el elemento 1, pero se mostrará la vista para el elemento 2. Y luego, cuando se desliza hacia la izquierda, va al elemento 1. Aunque ya estaba realmente en el elemento 1 ... ¿Qué tal un código para ayudar a aclarar las cosas?

La animación está rota, pero no tiene efectos secundarios extraños.

@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    super.startScroll(startX, startY, dx, dy, duration);
}

¡La animación funciona! Pero todo es extraño y aterrador ...

@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
    if (dx > 480 || dx < -480) {
    } else {
        super.startScroll(startX, startY, dx, dy, duration);
    }
}

La ÚNICA diferencia es que cuando se llama la segunda animación (más grande que el ancho de la pantalla de 480 píxeles), la ignoramos.

Después de leer el código fuente de Android para Scroller, descubrí que startScroll no comienza a desplazar nada. Configura todos los datos para ser desplazados, pero no inicia nada.

Mi corazonada

Cuando realiza la acción circular (1 a 7 o 7 a 1), hay dos llamadas a startScroll (). Creo que algo entre las dos llamadas está causando un problema.

El usuario se desplaza del elemento 1 al elemento 7, lo que provoca un salto de 0 a 7. Esto debería animarse hacia la izquierda.Se llama a startScroll () que indica una animación corta a la izquierda.LO QUE SUCEDE LO QUE ME HACE LLORAR PROBABLEMENTE QUE PIENSOSe llama a startScroll () indicando unlargo&nbsp;animación a laCorrecto.Larga animación a la derecha se produce.

Si comento 4, entonces 5 se convierte en "Animación corta correcta a la izquierda, las cosas se vuelven locas"

Resumen

Mi implementación de un ViewPager circular funciona, pero la animación está rota. Al intentar arreglar la animación, rompe la funcionalidad del ViewPager. Actualmente estoy girando mis ruedas tratando de averiguar cómo hacer que funcione. ¡Ayuadame! :)

Si algo no está claro, por favor comente a continuación y lo aclararé. Me doy cuenta de que no era muy precisa con la forma en que se rompen las cosas. Es difícil de describir porque ni siquiera está claro lo que estoy viendo en la pantalla. Si mi explicación es un problema en el que puedo trabajar, ¡házmelo saber!

Saludos, Coltin

Código

Este código se modificó levemente para hacerlo más legible por sí solo, aunque la funcionalidad es idéntica a mi iteración actual del código.

OnPageChangeListener.onPageSelected

@Override
public void onPageSelected(int _position) {
    boolean animate = true;
    if (_position < 1) {
        // Swiping left past the first element, go to element (9 - 2)=7
        setCurrentItem(getAdapter().getCount() - 2, animate);
    } else if (_position >= getAdapter().getCount() - 1) {
        // Swiping right past the last element
        setCurrentItem(1, animate);
    }
}

CircularScroller.startScroll

@Override
public void startScroll(int _startX, int _startY, int _dx, int _dy, int _duration) {
    // 480 is the width of the screen
    if (dx > 480 || dx < -480) {
        // Doing nothing in this block shows the correct animation,
        // but it causes the issues mentioned above

        // Uncomment to do the big scroll!
        // super.startScroll(_startX, _startY, _dx, _dy, _duration);

        // lastDX was to attempt to reset the scroll to be the previous
        // correct scroll distance; it had no effect
        // super.startScroll(_startX, _startY, lastDx, _dy, _duration);
    } else {
        lastDx = _dx;
        super.startScroll(_startX, _startY, _dx, _dy, _duration);
    }
}

CircularViewPageAdapter.CircularViewPageAdapter

private static final int m_Length = 7; // For our example only
private static Context m_Context;
private boolean[] created = null; // Not the best practice..

public CircularViewPageAdapter(Context _context) {
    m_Context = _context;
    created = new boolean[m_Length];
    for (int i = 0; i < m_Length; i++) {
        // So that we do not create things multiple times
        // I thought this was causing my issues, but it was not
        created[i] = false;
    }
}

CircularViewPageAdapter.getCount

@Override
public int getCount() {
    return m_Length + 2;
}

CircularViewPageAdapter.instantiateItem

@Override
public Object instantiateItem(View _collection, int _position) {

    int virtualPosition = getVirtualPosition(_position);
    if (created[virtualPosition - 1]) {
        return null;
    }

    TextView tv = new TextView(m_Context);
    // The first view is element 1 with label 0! :)
    tv.setText("Bonjour, merci! " + (virtualPosition - 1));
    tv.setTextColor(Color.WHITE);
    tv.setTextSize(30);

    ((ViewPager) _collection).addView(tv, 0);

    return tv;
}

CircularViewPageAdapter.destroyItem

@Override
public void destroyItem(ViewGroup container, int position, Object view) {
    ViewPager viewPager = (ViewPager) container;
    // If the virtual distance is distance 2 away, it should be destroyed.
    // If it's not intuitive why this is the case, please comment below
    // and I will clarify
    int virtualDistance = getVirtualDistance(viewPager.getCurrentItem(), getVirtualPosition(position));
    if ((virtualDistance == 2) || ((m_Length - virtualDistance) == 2)) {
        ((ViewPager) container).removeView((View) view);
        created[getVirtualPosition(position) - 1] = false;
    }
}