Napraw animację Circular ViewPager

Cel

Zbuduj Circular ViewPager.

Pierwszy element pozwala ci dotrzeć do ostatniego elementu i przesunąć do niego i odwrotnie. Powinieneś być w stanie przesuwać w dowolnym kierunku na zawsze.

To już zostało osiągnięte wcześniej, ale te pytania nie działają dla mojej implementacji. Oto kilka dla odniesienia:

jak utworzyć okrągły viewpager?ViewPager jako kolista kolejka / zawijaniehttps://github.com/antonyt/InfiniteViewPager

Jak próbowałem rozwiązać problem

Jako przykład użyjemy tablicy o rozmiarze 7. Elementy są następujące:

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

Gdy jesteś w elemencie 0, ViewPagers nie pozwalają ci przesuwać w lewo! Jak okropnie :(. Aby obejść to, dodałem 1 element do przodu i do końca.

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

Gdy ViewPageAdapter pyta o element 0 (instantiateItem ()), zwracamy element 7. Gdy ViewPageAdapter prosi o element 8, zwracamy element 1.

Podobnie w OnPageChangeListener w ViewPager, gdy onPageSelected jest wywoływany z 0, ustawiamy setCurrentItem (7), a gdy zostanie wywołany z 8, ustawimy setCurrentItem (1).

To działa.

Problem

Gdy przesuniesz w lewo od 1 do 0, a my ustawimy CururItem (7), animacja będzie przebiegać w prawo o 6 pełnych ekranów. To nie daje wyglądu okrągłego ViewPager, daje wrażenie, że pędzi do ostatniego elementu w przeciwnym kierunku niż żądany przez użytkownika za pomocą ruchu przesuwania!

To bardzo niepokojące.

Jak próbowałem to rozwiązać

Moją pierwszą skłonnością było wyłączenie płynnych (tzn. Wszystkich) animacji. Jest trochę lepiej, ale teraz jest niepewny, gdy przechodzisz od ostatniego elementu do pierwszego i odwrotnie.

Potem stworzyłem swój własny Scroller.

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

Odkryłem, że zawsze jest 1 wywołanie startScroll () podczas przenoszenia między elementami,z wyjątkiem kiedy przechodzę od 1 do 7 i od 7 do 1.

Pierwsze połączenie to prawidłowa animacja w kierunku i ilości.

Drugie połączenie to animacja, która przesuwa wszystko w prawo o wiele stron.

To jest, gdzie rzeczy stały się naprawdę trudne.

Myślałem, że rozwiązaniem będzie po prostu pominięcie drugiej animacji. Więc zrobiłem. Co się dzieje, to płynna animacja od 1 do 7 z 0 czkawkami. Idealny! Jeśli jednak przesuniesz, a nawet dotkniesz ekranu, nagle (bez animacji) pojawi się element 6! Jeśli przesunąłeś od 7 do 1, faktycznie będziesz w elemencie 2. Nie ma wywołania setCurrentItem (2) ani nawet wywołania OnPageChangeListener wskazującego, że dotarłeś do 2 w dowolnym momencie.

Ale tak naprawdę nie jesteś w elemencie 2, który jest dobry. Nadal jesteś w elemencie 1, ale zostanie wyświetlony widok elementu 2. A kiedy przesuniesz w lewo, przejdziesz do elementu 1. Nawet jeśli byłeś już w elemencie 1 już .. Co powiesz na kod, który pomoże wyjaśnić:

Animacja jest zepsuta, ale nie ma dziwnych efektów ubocznych

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

Animacja działa! Ale wszystko jest dziwne i przerażające ...

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

Jedyną różnicą jest to, że gdy wywoływana jest druga animacja (większa niż szerokość ekranu 480 pikseli), ignorujemy ją.

Po przeczytaniu kodu źródłowego Androida dla Scrollera odkryłem, że startScroll niczego nie przewija. Ustawia wszystkie dane do przewijania, ale niczego nie inicjuje.

Moje przeczucie

Gdy wykonujesz akcję kołową (od 1 do 7 lub od 7 do 1), są dwa wywołania funkcji startScroll (). Myślę, że coś pomiędzy dwoma połączeniami powoduje problem.

Użytkownik przewija od elementu 1 do elementu 7, powodując skok od 0 do 7. To powinno animować po lewej stronie.StartScroll () jest wywoływany, wskazując krótką animację po lewej stronie.DZIAŁA SIĘ, KTÓRY ZROBI SIĘ, ŻE PRAWDOPODOBNIE MYŚLĘStartScroll () jest nazywany wskazaniem adługie animacja dodobrze.Występuje długa animacja w prawo.

Jeśli komentuję 4, wtedy 5 staje się „Krótka poprawna animacja w lewo, rzeczy szaleją”

streszczenie

Moja implementacja Circular ViewPager działa, ale animacja jest zepsuta. Próbując naprawić animację, przerywa funkcjonalność ViewPager. Obecnie kręcę kołami, próbując dowiedzieć się, jak to zrobić. Pomóż mi! :)

Jeśli coś jest niejasne, proszę o komentarz poniżej, a ja wyjaśnię. Zdaję sobie sprawę, że nie byłam zbyt dokładna w kwestii tego, jak się rzeczy mają. Trudno to opisać, ponieważ nie jest nawet jasne, co widzę na ekranie. Jeśli moje wyjaśnienie jest problemem, mogę nad nim pracować, daj mi znać!

Pozdrawiam, Coltin

Kod

Ten kod jest nieco zmodyfikowany, aby był bardziej czytelny sam w sobie, chociaż funkcjonalność jest identyczna z moją obecną iteracją kodu.

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