Вопрос по android, android-viewpager – Исправить анимацию кругового ViewPager

15

Goal

Постройте Круговой ViewPager.

Первый элемент позволяет вам перейти к последнему элементу и провести по нему, и наоборот. Вы должны быть в состоянии провести в любом направлении навсегда.

Теперь это было сделано раньше, но эти вопросы не работают для моей реализации. Вот несколько для справки:

how to create circular viewpager? ViewPager as a circular queue / wrapping https://github.com/antonyt/InfiniteViewPager

How I Tried to Solve the Problem

Мы будем использовать массив размером 7 в качестве примера. Элементы следующие:

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

Когда вы находитесь в элементе 0, ViewPager не позволяет вам провести пальцем влево! Как ужасно :(. Чтобы обойти это, я добавил 1 элемент в начало и конец.

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

Когда ViewPageAdapter запрашивает элемент (instantiateItem ()) 0, мы возвращаем элемент 7. Когда ViewPageAdapter запрашивает элемент 8, мы возвращаем элемент 1.

Аналогично, в OnPageChangeListener в ViewPager, когда onPageSelected вызывается с 0, мы устанавливаемCurrentItem (7), а когда он вызывается с 8, мы устанавливаемCurrentItem (1).

Это работает.

The Problem

Когда вы проведете пальцем влево от 1 до 0, и мы установимCurrentItem (7), он будет анимирован полностью на 6 полных экранов. Это не создает видимости кругового ViewPager, оно придает внешний вид стремительному движению к последнему элементу в противоположном направлении, запрошенном пользователем при движении пальцем!

Это очень очень неприятно.

How I Tried to Solve This

Моим первым стремлением было отключить плавные (т.е. все) анимации. Это немного лучше, но теперь оно нестабильно, когда вы переходите от последнего элемента к первому и наоборот.

Затем я сделал свой собственный скроллер.

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

Я обнаружил, что при перемещении между элементами всегда есть 1 вызов startScroll (),except когда я перейду с 1 на 7 и с 7 на 1.

Первый звонок - правильная анимация по направлению и количеству.

Второй вызов - это анимация, которая перемещает все вправо на несколько страниц.

Вот где все стало действительно сложно.

Я думал, что решение было просто пропустить вторую анимацию. Так я и сделал. Происходит плавная анимация от 1 до 7 с 0 отклонениями. Отлично! Однако, если вы проведете пальцем по экрану или даже коснетесь экрана, вы внезапно (без анимации) окажетесь у элемента 6! Если вы проведете от 7 до 1, вы фактически окажетесь в элементе 2. Нет вызова setCurrentItem (2) или даже вызова OnPageChangeListener, указывающего, что вы достигли 2 в любой момент времени.

Но вы на самом деле не в элементе 2, что отчасти хорошо. Вы по-прежнему находитесь в элементе 1, но вид для элемента 2 будет показан. А затем, когда вы проводите влево, вы переходите к элементу 1. Даже если вы уже были на самом деле на элементе 1. Как насчет кода, который поможет прояснить ситуацию:

Animation is broken, but no weird side effects

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

Animation works! But everything is strange and scary...

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

ЕДИНСТВЕННОЕ отличие состоит в том, что когда вызывается вторая анимация (больше, чем ширина экрана в 480 пикселей), мы игнорируем ее.

Прочитав исходный код Android для Scroller, я обнаружил, что startScroll ничего не начинает прокручивать. Он устанавливает все данные, подлежащие прокрутке, но ничего не инициирует.

My Hunch

Когда вы выполняете циклическое действие (от 1 до 7 или от 7 до 1), есть два вызова startScroll (). Я думаю, что что-то между двумя вызовами вызывает проблему.

User scrolls from element 1 to element 7 causing a jump from 0 to 7. This should animate to the left. startScroll() is called indicating a short animation to the left. STUFF HAPPENS THAT MAKES ME CRY PROBABLY I THINK startScroll() is called indicating a long animation to the right. Long animation to the right occurs.

Если я закомментирую 4, то 5 станет «Короткая правильная анимация слева, все сойдет с ума»

Summary

Моя реализация Circular ViewPager работает, но анимация не работает. Попытка исправить анимацию нарушает функциональность ViewPager. В настоящее время я вращаю свои колеса, пытаясь понять, как заставить это работать. Помоги мне! :)

Если что-то неясно, пожалуйста, прокомментируйте ниже, и я уточню. Я понимаю, что был не очень точен в том, как все сломано. Это трудно описать, потому что даже не ясно, что я вижу на экране. Если мое объяснение - проблема, я могу работать над этим, дайте мне знать!

Ура, Колтин

Code

Этот код немного изменен, чтобы сделать его более читаемым сам по себе, хотя функциональность идентична моей текущей итерации кода.

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;
    }
}
Может тебя заинтересует. Как вы можете видеть на YouTube у меня нет проблем с анимацией :)stackoverflow.com/a/32980681/1052261 Dawid Drozd
& quot; Когда вы находитесь в элементе 0, ViewPager не позволяет вам провести пальцем влево! Как ужасно: (& quot; - Вы можете исправить это, вручную установив для текущей страницы значение больше 0 (то есть 1000), прежде чем она будет показана пользователю? Тогда они смогут прокручивать влево или вправо в течение длительного времени, прежде чем ударившись о "стену". Я использовал эту технику с бесконечным ListView в одной точке. Но никогда не ViewPager FoamyGuy
Если вы читаете дальше, это именно то, что я сделал. Вместо того, чтобы предполагать, что 0 должен был быть 0-м элементом, теперь он является триггером для перехода к «последнему» элемент. Я делаю то же самое с концом, где настоящий последний элемент - это триггер для перехода к «первому». который действительно элемент 1. Coltin

Ваш Ответ

1   ответ
1

Я думаю, что наилучшим выполнимым подходом было бы вместо использования обычного списка иметь обертку для списка, когдаget(pos) выполняется метод для получения объекта для создания представления, вы делаете что-то вроде этогоget(pos % numberOfViews) и когда он спрашивает размер списка, вы ставите, что списокInteger.MAX_VALUE и вы начинаете свой Список в середине его, так что вы можете сказать, что в большинстве случаев невозможно получить ошибку, если только они фактически не проведут в одну и ту же сторону, пока не дойдут до конца Списка. Позже я постараюсь опубликовать доказательство концепции этого слабого, если время позволит мне сделать это.

РЕДАКТИРОВАТЬ:

Я пробовал этот фрагмент кода, я знаю, что это простое текстовое поле, отображаемое на каждом представлении, но факт в том, что он работает отлично, он может быть медленнее в зависимости от общего количества просмотров, но доказательство концепции здесь. Что я сделал, так это то, чтоMAX_NUMBER_VIEWS представляет максимальное количество раз, которое пользователь может дать полностью, прежде чем его остановят. и, как вы можете видеть, я запустил viewpager на длину моего массива, так что это будет второй раз, когда он появляется, так что у вас есть один дополнительный ход влево и вправо, но вы можете изменить его по мере необходимости. Я надеюсь, что я не получу больше отрицательных моментов за решение, которое на самом деле работает.

ACTIVITY:

pager = (ViewPager)findViewById(R.id.viewpager);
String[] articles = {"ARTICLE 1","ARTICLE 2","ARTICLE 3","ARTICLE 4"};
pager.setAdapter(new ViewPagerAdapter(this, articles));
pager.setCurrentItem(articles.length);

ADAPTER:

public class ViewPagerAdapter extends PagerAdapter {

private Context ctx;
private String[] articles;
private final int MAX_NUMBER_VIEWS = 3;

public ViewPagerAdapter(Context ctx, String[] articles) {
    this.ctx = ctx;
    this.articles = articles.clone();
}

@Override
public int getCount() {
    return articles.length * this.MAX_NUMBER_VIEWS;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    TextView view = new TextView(ctx);
    view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                                          LayoutParams.MATCH_PARENT));
    int realPosition = position % articles.length;
    view.setText(this.articles[realPosition]);
    ((ViewPager) container).addView(view);
    return view;
}

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

@Override
public boolean isViewFromObject(View view, Object object) {
    return view == ((View) object);
}

@Override
public Parcelable saveState() {
    return null;
}

}
Если это сработает, это будет невероятно простым решением! Я попробую это на этих выходных и посмотрю, сработает ли это. Спасибо @Jorge! :) Coltin
Добро пожаловать @Coltin, если это работает или нет, пожалуйста, напишите это, чтобы сообщить другим, если это так или нет. удачи.
Это была и моя первая мысль. Однако после этого ViewPager стал невероятно медленным при инициализации. Казалось, что getPageWidh () вызывался для (пропущенных / виртуальных) страниц много раз. Вот почему я оказался в этой теме в поисках лучшего решения
@ Хорхе Агилар: «populate ()» и «addNewItem ()»; являются закрытыми методами, поэтому они не могут быть переопределены (если не изменены исходный код исходного ViewPager). в любом случае - я буду рад, если вы сможете опубликовать код, демонстрирующий ваше решение. это было бы очень полезно. заранее спасибо
Так было ли это полезно?

Похожие вопросы