Android 2 ViewPagers одновременная прокрутка

Возможно ли иметь 2 ViewPagers эта одновременная прокрутка вместе, если я начинаю прокручивать одну, другая выполняет ту же самую прокрутку. Или я должен реализовать что-то, кроме ViewPager.

благодарю вас

3 ответа

Решение, которое работало лучше всего для меня, состояло в том, чтобы пройти MotionEvent в OnTouchListener между ViewPager экземпляров. Пробовал фальшивое перетаскивание, но оно всегда было медленным и глючным (пробовал решение из этой темы тоже - не работало) - мне нужен был плавный эффект, похожий на параллакс.

Итак, мой совет заключается в реализации View.OnTouchListener, MotionEvent должен быть масштабирован, чтобы компенсировать разницу в ширине.

public class SyncScrollOnTouchListener implements View.OnTouchListener {

private final View syncedView;

public SyncScrollOnTouchListener(@NonNull View syncedView) {
    this.syncedView = syncedView;
}

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    MotionEvent syncEvent = MotionEvent.obtain(motionEvent);
    float width1 = view.getWidth();
    float width2 = syncedView.getWidth();

    //sync motion of two view pagers by simulating a touch event
    //offset by its X position, and scaled by width ratio
    syncEvent.setLocation(syncedView.getX() + motionEvent.getX() * width2 / width1,
            motionEvent.getY());
    syncedView.onTouchEvent(syncEvent);
    return false;
}
}

Затем установите его на свой ViewPager

    sourcePager.setOnTouchListener(new SyncScrollOnTouchListener(targetPager));

Обратите внимание, что это решение будет работать, только если оба пейджера имеют одинаковую ориентацию. Если вам это нужно для работы в разных ориентациях - настройте syncEvent Координата Y вместо X.

Есть еще одна проблема, которую мы должны принять во внимание - минимальная скорость перемещения и расстояние, при котором только один пейджер может изменить страницу.

Это можно легко исправить, добавив OnPageChangeListener на наш пейджер

sourcePager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset,
                                   int positionOffsetPixels) {
            //no-op
        }

        @Override
        public void onPageSelected(int position) {
            targetPager.setCurrentItem(position, true);
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //no-op
        }
    }); 

Вы можете дать каждому OnPageChangeListsner и реализовать onPageScrolled (а может и onPageSelected когда вы меняете страницы без прокрутки). Поскольку логика будет одинаковой, мы можем написать класс для этого:

public class ViewPagerScrollSync implements ViewPager.OnPageChangeListener {
    private ViewPager actor; // the one being scrolled
    private ViewPager target; // the one that needs to be scrolled in sync

    public ViewPagerScrollSync(ViewPager actor, ViewPager target) {
        this.actor = actor;
        this.target = target;
        actor.setOnPageChangeListener(this);
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (actor.isFakeDragging()) {
            return;
        }

        if (state == ViewPager.SCROLL_STATE_DRAGGING) {
            // actor has begun a drag
            target.beginFakeDrag();
        } else if (state == ViewPager.SCROLL_STATE_IDLE) {
            // actor has finished settling
            target.endFakeDrag();
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (actor.isFakeDragging()) {
            return;
        }
        if (target.isFakeDragging()) {
            // calculate drag amount in pixels.
            // i don't have code for this off the top of my head, but you'll probably
            // have to store the last position and offset from the previous call to
            // this method and take the difference.
            float dx = ...
            target.fakeDragBy(dx);
        }
    }

    @Override
    public void onPageSelected(int position) {
        if (actor.isFakeDragging()) {
            return;
        }

        // Check isFakeDragging here because this callback also occurs when
        // the user lifts his finger on a drag. If it was a real drag, we will
        // have begun a fake drag of the target; otherwise it was probably a
        // programmatic change of the current page.
        if (!target.isFakeDragging()) {
            target.setCurrentItem(position);
        }
    }
}

Затем в вашей Деятельности / Фрагмент вы должны сделать это:

ViewPager pager1 = ...
ViewPager pager2 = ...
ViewPagerScrollSync sync1 = new ViewPagerScrollSync(pager1, pager2);
ViewPagerScrollSync sync2 = new ViewPagerScrollSync(pager2, pager1);

Вам нужно будет следить за вашими позициями, чтобы избежать IndexOutOfBounds ошибки. В общем-то:

        viewPager1.setOnPageChangeListener(new OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            viewPager2.setCurrentItem(position);

        }

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onPageScrollStateChanged(int state) {
            // TODO Auto-generated method stub

        }
    });

Это будет работать при условии, что оба ViewPagers иметь одинаковое количество предметов. В противном случае вам нужно будет отслеживать свои позиции, чтобы синхронизировать поведение обоих пейджеров.

Уточнить: если ваш Adapters иметь различное количество элементов или вы не хотите, чтобы оба пейджера вели себя одинаково, вам нужно проверить положение viewPager1 в onPageSelected() метод, а затем отрегулируйте положение, чтобы перейти к setCurrentItem() метод viewPager2

Отбросьте этот класс в свой проект

import androidx.viewpager.widget.ViewPager;
/**
 * Sync Scroll 2 View Pager
 */
public class SyncScrollOnTouchListener implements ViewPager.OnPageChangeListener {

    private ViewPager master;
    private ViewPager slave;
    private int mScrollState = ViewPager.SCROLL_STATE_IDLE;

    public SyncScrollOnTouchListener(ViewPager master, ViewPager slave){
        this.master = master;
        this.slave = slave;
    }

    @Override
    public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
        if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
            return;
        }
        this.slave.scrollTo(this.master.getScrollX()*
                this.slave.getWidth()/
                this.master.getWidth(), 0);
    }

    @Override
    public void onPageSelected(final int position) {

    }

    @Override
    public void onPageScrollStateChanged(final int state) {
        mScrollState = state;
        if (state == ViewPager.SCROLL_STATE_IDLE) {
            this.slave.setCurrentItem(this.master
                    .getCurrentItem(), false);
        }
    }
}

Использование:

Теперь вы можете синхронизировать смену страниц просмотра страниц.

masterPager.addOnPageChangeListener(newSyncScrollOnTouchListener(masterPager,slavePager));

    //Sync Scroll of Pages
  
    leftPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(leftPanelPager,rightPanelPager));
    rightPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(rightPanelPager,leftPanelPager));
Другие вопросы по тегам