【问题标题】:Android 2 ViewPagers simultanious scrollAndroid 2 ViewPager 同时滚动
【发布时间】:2014-08-08 15:10:40
【问题描述】:

是否有可能有 2 个ViewPagers 同时滚动在一起,如果我开始滚动其中一个,另一个会执行完全相同的滚动行为。或者我应该实现 ViewPager 以外的东西。

谢谢

【问题讨论】:

  • 粘贴你的布局xml代码
  • 我还没有,我还在研究方法,谢谢
  • 在第一个片段中使用 OnPageChangeListener 并将其与第二个 ViewPager 同步。

标签: android android-viewpager


【解决方案1】:

最适合我的解决方案是在 OnTouchListener 中传递 MotionEventViewPager 实例之间。尝试过假拖动,但它总是很慢而且有问题(也尝试了这个线程的解决方案 - 没用) - 我需要一个平滑的、类似视差的效果。

所以,我的建议是实现View.OnTouchListenerMotionEvent 必须进行缩放以补偿宽度差异。

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

【讨论】:

    【解决方案2】:

    您可以给每个人一个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);
            }
        }
    }
    

    然后在你的 Activity/Fragment 中,你会这样做:

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

    【讨论】:

    • 看起来不错,我马上试试
    • 我为actor.isFakeDragging() 添加了一些检查以防止周期性回调。您应该尝试一下,看看它们是否有必要。
    • 你们真的成功了吗?我已经尝试过了,但它在 viewPagers 之间总是不同步。
    • 不错,优雅的解决方案。有两件事要补充:一是在onPageScrolled 中使用target.isFakeDragging 而不是!target.isFakeDragging 时我得到了这个工作;另一个是onPageScrolled 方法似乎被调用,而target 的假拖动仍然存在。这会导致target 切换到正确的位置,然后在拖动结束后立即切换回它开始的位置(如果“向前滚动”)。
    • 为了规避上一个问题,我为actor添加了一个endFakeDragsetCurrentItem,在onPageSelected当演员是假拖动并添加了一个target.isFakeDragging签入onPageScrollStateChanged,以便在不假拖动时不会调用endFakeDrag
    【解决方案3】:

    您需要密切注意自己的位置,以避免出现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 有不同数量的项目,或者您不希望两个寻呼机的行为完全相同,您需要在 onPageSelected() 方法中检查 viewPager1 的位置,然后调整位置传递给viewPager2setCurrentItem()方法

    【讨论】:

    【解决方案4】:

    在你的项目中删除这个类

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

    用法

    现在您可以同步查看页面的页面更改

    ma​​sterPager.addOnPageChangeListener(newSyncScrollOnTouchListener(ma​​sterPager,slavePager));

        //Sync Scroll of Pages
      
        leftPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(leftPanelPager,rightPanelPager));
        rightPanelPager.addOnPageChangeListener(new SyncScrollOnTouchListener(rightPanelPager,leftPanelPager));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-01
      • 2015-12-15
      相关资源
      最近更新 更多