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