Corrigir a animação de um ViewPager circular

Objetivo

Construa um ViewPager Circular.

O primeiro elemento permite chegar ao último elemento e passar para ele, e vice-versa. Você deve poder roubar em qualquer direção para sempre.

Agora isso foi feito antes, mas essas perguntas não funcionam para minha implementação. Aqui estão alguns para referência:

como criar viewpager circular?ViewPager como uma fila circular / quebra automáticahttps://github.com/antonyt/InfiniteViewPager

Como eu tentei resolver o problema

Nós usaremos uma matriz de tamanho 7 como exemplo. Os elementos são os seguintes:

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

Quando você está no elemento 0, os ViewPagers não permitem que você deslize para a esquerda! Que terrível :(. Para contornar isso, eu adicionei 1 elemento para a frente e fim.

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

Quando o ViewPageAdapter solicita o elemento 0 (instantiateItem ()), retornamos o elemento 7. Quando o ViewPageAdapter solicita o elemento 8, retornamos o elemento 1.

Da mesma forma no OnPageChangeListener no ViewPager, quando onPageSelected é chamado com 0, setCurrentItem (7), e quando é chamado com 8, setCurrentItem (1).

Isso funciona.

O problema

Quando você desliza para a esquerda de 1 para 0, e nós setCurrentItem (7), ele irá animar todo o caminho para a direita por 6 telas cheias. Isso não dá a aparência de um ViewPager circular, ele dá a aparência correndo para o último elemento na direção oposta que o usuário solicitou com seu movimento de furto!

Isso é muito muito chocante.

Como eu tentei resolver isso

Minha primeira inclinação foi desativar animações suaves (ou seja, todas). É um pouco melhor, mas agora está instável quando você passa do último elemento para o primeiro e vice-versa.

Eu então fiz meu próprio Scroller.

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

O que eu encontrei foi que sempre há uma chamada para startScroll () ao mover entre elementos,exceto quando eu passar de 1 para 7 e 7 para 1.

A primeira chamada é a animação correta em direção e quantidade.

A segunda chamada é a animação que move tudo para a direita por várias páginas.

É aqui que as coisas ficaram realmente complicadas.

Eu pensei que a solução era simplesmente pular a segunda animação. Então eu fiz. O que acontece é uma animação suave de 1 a 7 com 0 soluços. Perfeito! No entanto, se você deslizar, ou mesmo tocar na tela, você está de repente (sem animação) no elemento 6! Se você tivesse passado de 7 para 1, você realmente estará no elemento 2. Não há nenhuma chamada para setCurrentItem (2) ou até mesmo uma chamada para o OnPageChangeListener indicando que você chegou em 2 a qualquer momento.

Mas você não está realmente no elemento 2, o que é bom. Você ainda está no elemento 1, mas a exibição do elemento 2 será mostrada. E então, quando você desliza para a esquerda, você vai para o elemento 1. Mesmo que você já estivesse realmente no elemento 1 ... Que tal um código para ajudar a esclarecer as coisas:

A animação está quebrada, mas não há efeitos colaterais estranhos

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

Obras de animação! Mas tudo é estranho e assustador ...

@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);
    }
}

A ÚNICA diferença é que quando a segunda animação (maior que a largura da tela de 480 pixels) é chamada, nós a ignoramos.

Depois de ler o código fonte do Android para o Scroller, descobri que startScroll não inicia a rolagem de nada. Ele configura todos os dados a serem rolados, mas não inicia nada.

Meu palpite

Quando você faz a ação circular (1 a 7 ou 7 a 1), há duas chamadas para startScroll (). Eu acho que algo entre as duas chamadas está causando um problema.

O usuário rola do elemento 1 para o elemento 7, causando um salto de 0 a 7. Isso deve animar à esquerda.startScroll () é chamado indicando uma animação curta à esquerda.MATERIAL ACONTECE QUE ME FAZ CRY PROBABLY EU PENSOstartScroll () é chamado indicando umlongo animação para ocerto.Long animação para a direita ocorre.

Se eu comentar 4, então 5 se torna "Curta animação correta para a esquerda, as coisas ficam loucas"

Resumo

Minha implementação de um ViewPager circular funciona, mas a animação está quebrada. Ao tentar consertar a animação, ela quebra a funcionalidade do ViewPager. Atualmente estou girando minhas rodas tentando descobrir como fazê-lo funcionar. Ajude-me! :)

Se alguma coisa não estiver clara, por favor comente abaixo e eu vou esclarecer. Eu percebo que não fui muito preciso em como as coisas estão quebradas. É difícil descrever porque não está claro o que estou vendo na tela. Se minha explicação for um problema, posso trabalhar nisso, me avise!

Felicidades, Coltin

Código

Esse código é ligeiramente modificado para torná-lo mais legível por si só, embora a funcionalidade seja idêntica à minha iteração atual do 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;
    }
}

questionAnswers(1)

yourAnswerToTheQuestion