Intereting Posts
Как установить обратный вызов, когда WebView предоставил данные (готов к отображению) Как увеличить пространство между текстом и подчеркнуть в TextView Android? Google maps api не помещает маркер в прекрасное место Альтернатива для устаревших AudioManger.setStreamMute? Android: как интегрировать декодер в мультимедийную инфраструктуру Не удалось разрешить: com.google.firebase: firebase-messaging: 10.0.0 Android – доступ к локальному хосту из браузера без подключения к Интернету Android: ClassNotFoundException при передаче сериализуемого объекта в Activity Вращение вверх дном не приводит к изменению конфигурации Анимация элемента GridView путем прокрутки Отправьте файл PDF в виде приложения «Почта» или «Приложение», чтобы напрямую просмотреть файл Получение предупреждений с помощью Proguard (с внешними библиотеками) Службы Google Play устарели. Требуется 10084000, но найдено 9879470. Не удается обновить Создание меню всплывающих опций в контекстной панели действий Whatsapp как сворачивание панели инструментов

Синхронизация двух ViewPagers с помощью OnPageChangeListener

Я пытаюсь синхронизировать два ViewPager s, поскольку, по-видимому, у меня довольно много людей, и я дошел до этого:

 private ViewPager mNavPager; private ViewPager mMainPager; private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() { private boolean mNavDragging; private int mScrollPosition; @Override public void onPageSelected(int position) { mScrollPosition = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(mNavDragging) mMainPager.scrollTo(positionOffsetPixels, 0); } @Override public void onPageScrollStateChanged(int state) { switch(state) { case ViewPager.SCROLL_STATE_DRAGGING: case ViewPager.SCROLL_STATE_SETTLING: mNavDragging = true; break; case ViewPager.SCROLL_STATE_IDLE: mNavDragging = false; break; } } }; private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() { private boolean mMainDragging; private int mScrollPosition; @Override public void onPageSelected(int position) { mScrollPosition = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(mMainDragging) mNavPager.scrollTo(positionOffsetPixels, 0); } @Override public void onPageScrollStateChanged(int state) { switch(state) { case ViewPager.SCROLL_STATE_DRAGGING: case ViewPager.SCROLL_STATE_SETTLING: mMainDragging = true; break; case ViewPager.SCROLL_STATE_IDLE: mMainDragging = false; break; } } }; 

Если один из них прокручивается вручную, другой подчинен ему, используя свойство состояния прокрутки. Он прекрасно работает, пока предметы не достигнут своей конечной позиции; На данный момент подчиненный пейджер мгновенно возвращается к ранее выбранному элементу, как будто прокрутка не состоялась.

Я попытался вызвать ViewPager#setCurrentItem(mScrolledPosition) с множеством различных логических ограничений, но это тоже не работает, хотя иногда это делает его хуже. Мне кажется, что с этим можно что-то сделать, но я не уверен, что.

Как я могу заставить подчиненный пейджер оставаться в правильном положении?

Solutions Collecting From Web of "Синхронизация двух ViewPagers с помощью OnPageChangeListener"

Я решил эту проблему гораздо проще (и чище) с помощью OnPageChangeListener:

 mViewPager1.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { private int mScrollState = ViewPager.SCROLL_STATE_IDLE; @Override public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { return; } mViewPager2.scrollTo(mViewPager1.getScrollX(), mViewPager2.getScrollY()); } @Override public void onPageSelected(final int position) { } @Override public void onPageScrollStateChanged(final int state) { mScrollState = state; if (state == ViewPager.SCROLL_STATE_IDLE) { mViewPager2.setCurrentItem(mViewPager1.getCurrentItem(), false); } } }); mViewPager2.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { private int mScrollState = ViewPager.SCROLL_STATE_IDLE; @Override public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) { if (mScrollState == ViewPager.SCROLL_STATE_IDLE) { return; } mViewPager1.scrollTo(mViewPager2.getScrollX(), mViewPager1.getScrollY()); } @Override public void onPageSelected(final int position) { } @Override public void onPageScrollStateChanged(final int state) { mScrollState = state; if (state == ViewPager.SCROLL_STATE_IDLE) { mViewPager1.setCurrentItem(mViewPager2.getCurrentItem(), false); } } }); 

Я решил проблему без использования Listener. Таким образом, вы можете использовать прослушиватель для некоторых других вещей и сделать код более чистым.

Я знаю, что его вопрос задал давно. Я искал решение и сам решил.

Вот как мой пользовательский код ViewPager

 public class CustomPager extends ViewPager { CustomPager mCustomPager; private boolean forSuper; public CustomPager(Context context) { super(context); } public CustomPager(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean onInterceptTouchEvent(MotionEvent arg0) { if (!forSuper) { mCustomPager.forSuper(true); mCustomPager.onInterceptTouchEvent(arg0); mCustomPager.forSuper(false); } return super.onInterceptTouchEvent(arg0); } @Override public boolean onTouchEvent(MotionEvent arg0) { if (!forSuper) { mCustomPager.forSuper(true); mCustomPager.onTouchEvent(arg0); mCustomPager.forSuper(false); } return super.onTouchEvent(arg0); } public void setViewPager(CustomPager customPager) { mCustomPager = customPager; } public void forSuper(boolean forSuper) { this.forSuper = forSuper; } @Override public void setCurrentItem(int item, boolean smoothScroll) { if (!forSuper) { mCustomPager.forSuper(true); mCustomPager.setCurrentItem(item, smoothScroll); mCustomPager.forSuper(false); } super.setCurrentItem(item, smoothScroll); } @Override public void setCurrentItem(int item) { if (!forSuper) { mCustomPager.forSuper(true); mCustomPager.setCurrentItem(item); mCustomPager.forSuper(false); } super.setCurrentItem(item); } } 

И вы должны установить такие пейджеры, как это, прежде чем адаптер будет установлен и сразу после получения справки, используя идентификатор.

 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); backPager=(CustomPager) findViewById(R.id.BackPager); frontPager = (CustomPager) findViewById(R.id.frontPager); frontPager.setViewPager(backPager); backPager.setViewPager(frontPager); backPager.setAdapter(your Adapter); frontPager.setAdapter(your Adapter); } 

Перед назначением адаптера необходимо установить ViewPagers.

Это делает все правильно, за исключением того, что иногда пропускает очень быстрые щелчки на слабом представлении. По какой-то причине, включая фальшивые события перетаскивания во время фазы расчета, возникают реальные проблемы.

 private ViewPager mNavPager; private ViewPager mMainPager; private final OnPageChangeListener mNavPagerListener = new OnPageChangeListener() { private int mLastScrollPosition; private int mLastPagePosition; @Override public void onPageSelected(int position) { mLastPagePosition = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(mMainPager.isFakeDragging()) { int absoluteOffsetPixels = positionOffsetPixels; if(mLastPagePosition!=position) { absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth(); mLastPagePosition = position; } Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels)); mMainPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels); mLastScrollPosition = positionOffsetPixels; } } @Override public void onPageScrollStateChanged(int state) { if(!mNavPager.isFakeDragging()) { switch(state) { case ViewPager.SCROLL_STATE_DRAGGING: if(!mMainPager.isFakeDragging()) mMainPager.beginFakeDrag(); break; case ViewPager.SCROLL_STATE_SETTLING: case ViewPager.SCROLL_STATE_IDLE: if(mMainPager.isFakeDragging()) { mMainPager.endFakeDrag(); mLastScrollPosition = 0; } break; } } } }; private OnPageChangeListener mMainPagerListener = new OnPageChangeListener() { private int mLastScrollPosition; private int mLastPagePosition; @Override public void onPageSelected(int position) { mLastPagePosition = position; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if(mNavPager.isFakeDragging()) { int absoluteOffsetPixels = positionOffsetPixels; if(mLastPagePosition!=position) { absoluteOffsetPixels += (position - mLastPagePosition) * mMainPager.getWidth(); mLastPagePosition = position; } Log.d(TAG, "fake nav drag by " + (mLastScrollPosition - absoluteOffsetPixels)); mNavPager.fakeDragBy(mLastScrollPosition - absoluteOffsetPixels); mLastScrollPosition = positionOffsetPixels; } } @Override public void onPageScrollStateChanged(int state) { if(!mMainPager.isFakeDragging()) { switch(state) { case ViewPager.SCROLL_STATE_DRAGGING: if(!mNavPager.isFakeDragging()) mNavPager.beginFakeDrag(); break; case ViewPager.SCROLL_STATE_SETTLING: case ViewPager.SCROLL_STATE_IDLE: if(mNavPager.isFakeDragging()) { mNavPager.endFakeDrag(); mLastScrollPosition = 0; } break; } } } }; 

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

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

Тем не менее, можно использовать модифицированный PagerTitleStrip для получения точного отслеживания ViewPager и для передачи событий касания, захваченных полосой, непосредственно в ViewPager .

Источник PagerTitleStrip находится здесь.

В общем, что нужно сделать для выполнения этой работы: заменить mPrevText , mCurrText и mNextText с представлениями типа, который вы хотите использовать; Удалить функции onAttachedToWindow() и onDetachedFromWindow() ; Удалите вызовы в PagerAdapter которые PagerAdapter к наблюдателям набора данных, и добавьте OnTouchListener в модифицированную полосу, которая поддельно перетаскивает основной пейджер. Вам также нужно будет добавить измененную полосу заголовка как OnPageChangeListener в ViewPager поскольку внутренние прослушиватели не видны вне пакета.

Это гигантская боль? Да. Но это работает. Я напишу об этом более подробно в ближайшее время.

С помощью всех других ответов я создал класс, чтобы его можно было легко реализовать.

  public class OnPageChangeListernerSync implements ViewPager.OnPageChangeListener { private ViewPager master; private ViewPager slave; private int mScrollState = ViewPager.SCROLL_STATE_IDLE; public OnPageChangeListernerSync(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); } } } 

 // In your activity this.upperPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.upperPager, this.lowerPager)); this.lowerPager.addOnPageChangeListener(new OnPageChangeListernerSync(this.lowerPager, this.upperPager)); 

У меня была аналогичная проблема, также мне нужно было объединить мою синхронизацию с анимациями в PageTransformer .

Первоначально этот ответ был указан здесь: https://stackoverflow.com/a/43638796/2867886

Но я приложу его сюда для удобства.

Лучшим решением для меня было передать 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 } }); 

В любой ситуации все ответы примерно точны. Здесь я даю еще один ответ, который будет использовать для одновременного перемещения обоих ViewPagers, независимо от того, является ли этот размер таким же или нет :

 viewPagerBanner.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { private int scrollState = ViewPager.SCROLL_STATE_IDLE; // Indicates that the pager is in an idle, settled state. // The current page is fully in view and no animation is in progress. @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (scrollState == ViewPager.SCROLL_STATE_IDLE) { return; } viewPagerTitle.scrollTo(viewPagerBanner.getScrollX()* viewPagerTitle.getWidth()/ viewPagerBanner.getWidth(), 0); // We are not interested in Y axis position } @Override public void onPageSelected(int position) {} @Override public void onPageScrollStateChanged(int state) { scrollState = state; if (state == ViewPager.SCROLL_STATE_IDLE) { viewPagerTitle.setCurrentItem(viewPagerBanner.getCurrentItem(), false); } } });