如何在 2 个 ViewPagers 之间产生视差效果? [英] How to have a parallax effect between 2 ViewPagers?

查看:57
本文介绍了如何在 2 个 ViewPagers 之间产生视差效果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下场景:

  1. 有 2 个 viewPager,每个宽度和高度都不同,但它们的页数完全相同.
  2. 拖动一个应该拖动另一个,所以效果就像一个视差"效果.
  3. 与#2 相同,但对于另一个 viewPager.
  4. viewpagers 之间还有一个自动切换机制,所以每 2 秒它会自动切换到下一页(如果在最后一页,则切换回第一页).

问题

我想我的一些功能可以工作,但有一些问题:

The problem

I think I got some functionality working, but it has some issues:

  1. 这只是简单的 XML.这里没什么特别的.
  2. 我使用了 'OnPageChangeListener' 并且在那里,在 'onPageScrolled' 中,我使用了假拖拽.问题是,如果用户的拖动停止在页面中间附近(从而激活自动向左/向右滚动 viewPager),假拖动会失去同步,并且可能会发生奇怪的事情:错误的页面甚至停留在页面之间...
  3. 目前,我不会通过用户的拖动处理另一个 viewPager,但这也可能是一个问题.
  4. 当我成功解决 #2(或 #3)时,这可能是一个问题.现在,我使用的处理程序使用可运行并在内部调用此命令:

  1. This is just simple XML. nothing special here.
  2. I've used 'OnPageChangeListener' and there, in 'onPageScrolled' , I've used fake-dragging. Problem is, if the dragging of the user stops near the middle of the page (thus activates auto-scrolling the viewPager left/right), the fake dragging loses its sync, and weird things can occur: wrong page or even staying between pages...
  3. For now, I don't handle the other viewPager with the dragging of the user, but it might be an issue too.
  4. this can be an issue when I succeed solving #2 (or #3). For now, I'm using a handler that use a runnable and calls this command inside :

mViewPager.setCurrentItem((mViewPager.getCurrentItem() + 1) % mViewPager.getAdapter().getCount(), true);

我尝试过的

我尝试过这篇文章,但效果不佳,因为当用户在完成切换到另一个页面之前停止拖动时,它遇到了同样的问题.

What I've tried

I've tried this post, but it didn't work well, as it got the same issues when the user-dragging stops before finished swithching to another page.

唯一有效的类似解决方案是使用单个 ViewPager(此处),但我需要使用 2 个 ViewPagers,每个都相互影响.

Only similar solution that works, is with a single ViewPager (here), but I need to work with 2 ViewPagers, each affects the other.

所以我自己尝试过:假设一个 viewPager 是viewPager",另一个(应该跟在viewPager"之后)是viewPager2",这就是我所做的:

So I've tried doing it myself: suppose one viewPager is "viewPager", and the other (that is supposed to follow "viewPager") is "viewPager2" , this is what I've done:

    viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        int lastPositionOffsetPixels=Integer.MIN_VALUE;

        @Override
        public void onPageScrolled(final int position, final float positionOffset, final int positionOffsetPixels) {
            if (!viewPager2.isFakeDragging())
                viewPager2.beginFakeDrag();
            if(lastPositionOffsetPixels==Integer.MIN_VALUE) {
                lastPositionOffsetPixels=positionOffsetPixels;
                return;
            }
            viewPager2.fakeDragBy((lastPositionOffsetPixels - positionOffsetPixels) * viewPager2.getWidth() / viewPager.getWidth());
            lastPositionOffsetPixels = positionOffsetPixels;
        }

        @Override
        public void onPageSelected(final int position) {
            if (viewPager2.isFakeDragging())
                viewPager2.endFakeDrag();
            viewPager2.setCurrentItem(position,true);
        }

        @Override
        public void onPageScrollStateChanged(final int state) {
        }
    });

我选择使用假拖动是因为 甚至文档都说它可能对这种完全相同的场景有用:

I've chosen to use fake-dragging because even the docs say it could be useful for this exact same scenario :

如果您想同步物体的运动,假拖拽会很有用ViewPager 与另一个视图的触摸滚动,同时仍然让 ViewPager 控制捕捉动作和甩动行为.(例如视差滚动选项卡.)调用 fakeDragBy(float) 来模拟实际拖动运动.调用 endFakeDrag() 完成假拖拽必要时投掷.

A fake drag can be useful if you want to synchronize the motion of the ViewPager with the touch scrolling of another view, while still letting the ViewPager control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.) Call fakeDragBy(float) to simulate the actual drag motion. Call endFakeDrag() to complete the fake drag and fling as necessary.

为了便于测试,这里有更多代码:

To make it easy to test, here's more code:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager);
    final ViewPager viewPager2 = (ViewPager) findViewById(R.id.viewPager2);
    viewPager.setAdapter(new MyPagerAdapter());
    viewPager2.setAdapter(new MyPagerAdapter());
    //do here what's needed to make "viewPager2" to follow dragging on "viewPager"
    }

private class MyPagerAdapter extends PagerAdapter {
    int[] colors = new int[]{0xffff0000, 0xff00ff00, 0xff0000ff};

    @Override
    public int getCount() {
        return 3;
    }

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

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

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        TextView textView = new TextView(MainActivity.this);
        textView.setText("item" + position);
        textView.setBackgroundColor(colors[position]);
        textView.setGravity(Gravity.CENTER);
        final LayoutParams params = new LayoutParams();
        params.height = LayoutParams.MATCH_PARENT;
        params.width = LayoutParams.MATCH_PARENT;
        params.gravity = Gravity.CENTER;
        textView.setLayoutParams(params);
        textView.setTextColor(0xff000000);
        container.addView(textView);
        return textView;
    }
}

activity_main

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="viewPager:"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="viewPager2:"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager2"
        android:layout_width="match_parent"
        android:layout_height="100dp"/>

</LinearLayout>

问题

代码几乎运行良好.我只需要知道,为了避免(由用户)停止拖动的问题,缺少什么?其他问题可能有更简单的解决方案.

The question

The code almost works well. I just need to know, what is missing to avoid the issues of stopping the dragging (by the user)? The other issues might have easier solutions.

推荐答案

一个可能的解决方案

经过大量工作,我们找到了一个几乎没有错误的解决方案.有一个罕见的错误,即在滚动时,另一个 viewPager 会闪烁.奇怪的是,它仅在用户滚动第二个 viewPager 时发生.所有其他尝试都有其他问题,例如在完成滚动到新页面时出现空白页面或跳跃"/橡胶"效果.

A possible solution

After a lot of work, we've found a solution that almost has no bugs. There is a rare bug that while scrolling, the other viewPager flashes. Weird thing is that it happens only when the user scrolls the second viewPager. All other tries had other issues, like empty page or "jumpy"/"rubber" effect when finishing the scrolling to a new page.

代码如下:

private static class ParallaxOnPageChangeListener implements ViewPager.OnPageChangeListener {
    private final AtomicReference<ViewPager> masterRef;
    /**
     * the viewpager that is being scrolled
     */
    private ViewPager viewPager;
    /**
     * the viewpager that should be synced
     */
    private ViewPager viewPager2;
    private float lastRemainder;
    private int mLastPos = -1;

    public ParallaxOnPageChangeListener(ViewPager viewPager, ViewPager viewPager2, final AtomicReference<ViewPager> masterRef) {
        this.viewPager = viewPager;
        this.viewPager2 = viewPager2;
        this.masterRef = masterRef;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        final ViewPager currentMaster = masterRef.get();
        if (currentMaster == viewPager2)
            return;
        switch (state) {
            case ViewPager.SCROLL_STATE_DRAGGING:
                if (currentMaster == null)
                    masterRef.set(viewPager);
                break;
            case ViewPager.SCROLL_STATE_SETTLING:
                if (mLastPos != viewPager2.getCurrentItem())
                    viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
                break;
            case ViewPager.SCROLL_STATE_IDLE:
                masterRef.set(null);
                viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
                mLastPos = -1;
                break;
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (masterRef.get() == viewPager2)
            return;
        if (mLastPos == -1)
            mLastPos = position;
        float diffFactor = (float) viewPager2.getWidth() / this.viewPager.getWidth();
        float scrollTo = this.viewPager.getScrollX() * diffFactor + lastRemainder;
        int scrollToInt = scrollTo < 0 ? (int) Math.ceil(scrollTo) : (int) Math.floor(scrollTo);
        lastRemainder = scrollToInt - scrollTo;
        if (mLastPos != viewPager.getCurrentItem())
            viewPager2.setCurrentItem(viewPager.getCurrentItem(), false);
        viewPager2.scrollTo(scrollToInt, 0);

    }

    @Override
    public void onPageSelected(int position) {
    }
}

用法:

    /**the current master viewPager*/
    AtomicReference<ViewPager> masterRef = new AtomicReference<>();
    viewPager.addOnPageChangeListener(new ParallaxOnPageChangeListener(viewPager, viewPager2, masterRef));
    viewPager2.addOnPageChangeListener(new ParallaxOnPageChangeListener(viewPager2, viewPager, masterRef));

对于自动切换,原始代码工作正常.

For the auto-switching, the original code works fine.

由于它很难测试,而且我想让你们更容易尝试,这里是一个 Github 存储库:

Since it's quite hard to test it, and I want to make it easier for you guys to try it out, here's a Github repo:

[https://github.com/AndroidDeveloperLB/ParallaxViewPagers][4]

[https://github.com/AndroidDeveloperLB/ParallaxViewPagers][4]

请注意,正如我所提到的,它仍然存在问题.主要是不时的闪烁"效果,但不知为何,只有在第二个ViewPager上滚动时才出现.

Do note that as I've mentioned, it still has issues. Mainly the "flashing" effect from time to time, but for some reason, only when scrolling on the second ViewPager.

这篇关于如何在 2 个 ViewPagers 之间产生视差效果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆