49

我正在 SDK 4.03、Samsung Infuse Android 2.2、Android 4 支持库上编译,并在我的应用程序中使用 ViewPager,实际滑动工作正常,但是当我这样做时

viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);  

它不会平滑滚动,但会立即切换视图。尽管文档明确指出,这是将第二个参数设置为 true 的目的。这是怎么回事?

4

13 回答 13

43

我通过创建一个使用反射覆盖 ViewPager.mScroller 的 MyViewPager 解决了这个问题。

public class MyViewPager extends ViewPager {

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        setMyScroller();
    }

    private void setMyScroller() {
        try {
            Class<?> viewpager = ViewPager.class;
            Field scroller = viewpager.getDeclaredField("mScroller");
            scroller.setAccessible(true);
            scroller.set(this, new MyScroller(getContext()));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public class MyScroller extends Scroller {
        public MyScroller(Context context) {
            super(context, new DecelerateInterpolator());
        }

        @Override
        public void startScroll(int startX, int startY, int dx, int dy, int duration) {
            super.startScroll(startX, startY, dx, dy, 1000 /*1 secs*/);
        }
    }
}
于 2013-03-21T14:07:40.980 回答
16

这就是我所做的。我通过将我自己的自定义子类放在同一个包smoothScrollTo中来覆盖包私有方法。ViewPager它被传递一个零值,这会导致捕捉行为而不是平滑滚动。

package android.support.v4.view;

import android.content.Context;
import android.util.AttributeSet;

public class MyViewPager extends ViewPager {

    public MyViewPager(Context context) {
        super(context);
    }

    public MyViewPager(Context context, AttributeSet attr) {
        super(context, attr);
    }

    void smoothScrollTo(int x, int y, int velocity) {
        super.smoothScrollTo(x, y, 1);
    }
}

效果很好,如果您愿意,您可以计算并提供仅为 1 的实际速度 ISO。

于 2013-02-08T15:45:42.470 回答
14

setCurrentItem在处理程序中调用了函数,它对我来说很好。

new Handler().post(new Runnable() {
        @Override
        public void run() {
            myViewPager.setCurrentItem(1, true);
        }
    });

希望这可以帮助。

于 2015-11-02T09:06:28.183 回答
6

我知道这个帖子很老了,但这是谷歌搜索结果中的佼佼者之一。我一直在反复讨论如何解决这个问题。上述解决方案都没有帮助我。但是,我确实找到了适合我的解决方案。

我的设置目前看起来是 viewpager 中的列表视图。当您单击其中一个视图时,它会创建一个新页面并滚动到该页面。这以前很活泼,但似乎这是因为我在打电话

mViewPager.setCurrentItem(intIndex, true);

从我的 OnClickEvent 内部。由于某种原因,viewpager 不喜欢这个,所以我做了这个函数。它创建一个在 UI 线程上运行 runnable 的新线程。这个可运行文件告诉 ViewPager 滚动到某个项目。

public static void scrollToIndex(int index) {

    final int intIndex = index;

    //We're going to make another thread to separate ourselves from whatever
    //thread we are in 
    Thread sepThread = new Thread(new Runnable(){

        public void run()
        {
            //Then, inside that thread, run a runnable on the ui thread.
            //MyActivity.getContext() is a static function that returns the 
            //context of the activity. It's useful in a pinch.
            ((Activity)MyActivity.getContext()).runOnUiThread(new Runnable(){

                @Override
                public void run() {

                    if (mSlidingTabLayout != null)
                    {
                        //I'm using a tabstrip with this as well, make sure
                        //the tabstrip is updated, or else it won't scroll
                        //correctly
                        if(mSlidingTabLayout.getTabStripChildCount() <= intIndex)
                        mSlidingTabLayout.updateTabStrip();

                        //Inside this thread is where you call setCurrentItem
                        mViewPager.setCurrentItem(intIndex, true);

                    }

                }

            });

         }
    });


    sepThread.start();


}

我希望我至少帮助了解决这个问题的人。祝你好运!

编辑:查看我的答案,我很确定你可以在 ui 线程上运行,它应该可以工作。不过,我还没有测试过。

于 2015-06-19T15:39:10.020 回答
2

我创建了这个类,因为我对上述解决方案并不完全满意

该类覆盖setCurrentItem(int item, boolean smoothScroll)并使用反射来尽可能保持此方法的原始性。关键是速度不是 0。你只需ViewPager要用这个MyViewPager类替换你的 current 并像往常一样使用它:

import android.content.Context;
import android.util.AttributeSet;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import androidx.viewpager.widget.ViewPager;

public class MyViewPager extends ViewPager {

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    /**
     * Set the currently selected page. If the ViewPager has already been through its first
     * layout with its current adapter there will be a smooth animated transition between
     * the current item and the specified item.
     *
     * @param item         Item index to select
     * @param smoothScroll True to smoothly scroll to the new item, false to transition immediately
     */
    @Override
    public void setCurrentItem(int item, boolean smoothScroll) {
        final Class<?> viewpager = ViewPager.class;
        int velocity = 1;

        try {
            Field mPopulatePending = viewpager.getDeclaredField("mPopulatePending");
            mPopulatePending.setAccessible(true);
            mPopulatePending.set(this, false);

            Method setCurrentItemInternal = viewpager.getDeclaredMethod("setCurrentItemInternal", int.class, boolean.class, boolean.class, int.class);
            setCurrentItemInternal.setAccessible(true);
            setCurrentItemInternal.invoke(this, item, smoothScroll, false, velocity);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
于 2018-11-05T15:47:08.003 回答
0

对于那些使用 Xamarin 的人(尽管这种方法也适用于 Java),我可以建议根据上面的答案使用下一种方法(Android Support Library v7 AppCompat 19.1.0 中的 ViewPager):

public void ScrollSmooth(int i)
{
    var jClass = JNIEnv.GetObjectClass(_pager.Handle);
    var jMethod = JNIEnv.GetMethodID(jClass, "setCurrentItemInternal", "(IZZI)V");
    JNIEnv.CallVoidMethod (_pager.Handle, jMethod, new JValue (i), new JValue (true), new JValue (false), new JValue (1));
}

它依赖于来自http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.1_r1/android/support/v4/view/ViewPager.java的 ViewPager 实现

于 2014-07-25T00:57:35.160 回答
0

ViewPager 从修订版 5 到修订版 9 发生了很大变化。其中一些更改可能与您的问题有关:

  • ViewPager 中用户界面行为的错误修复(修订版 5 和 6)
  • 修复了 ViewPager 的许多错误(修订版 9)

鉴于您的代码应该可以正常工作,我最好的猜测是您的支持库不是最新的。如果不是,请尝试更新库。

于 2012-08-15T00:39:07.553 回答
0

我有同样的问题,但今天我找到了一个简单的解决方案。也许它会帮助你。首先,假设我们有一个填满整个屏幕的 ViewPager。为了在页面之间切换,我创建了自己的带有标签的自定义视图并将其放在 ViewPager 上。单击选项卡应该使用 setCurrentItem(item, true) 平滑地滚动到 ViewPager 中的相应页面 - 但它会立即滚动,没有平滑!然后我尝试在 ViewPager + 回调上添加一个简单的按钮(不是自定义的):

@Override
public void onClick(View v) {
   viewPager.setCurrentItem(item, true);
}

在那之后,平滑的卷轴开始工作。因此,解决方案非常简单:在 Button 类内部,内部 boolean onTouch(...) 侦听器返回 true,因此它总是消耗触摸事件!然后,当我在内部布尔 onTouch(...) 侦听器中将“return false”替换为“return true”时,平滑滚动开始为我的自定义选项卡视图工作。

我希望我的成功故事可以帮助到你。

于 2012-08-16T09:06:53.177 回答
0

下面的代码将暂停页面片刻(500 毫秒)然后滑动到下一个。

import androidx.viewpager.widget.ViewPager;


new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            viewPager.setCurrentItem(index, true);
        }
    }, 500);
于 2020-05-23T11:46:37.160 回答
0

注意这个变量mFirstLayout- viewpager 回调时会被设置为 true onAttachedToWindow(比如在 recyclerview 上),所以不会是smoothScroll。您应该覆盖 onAttachedToWindow 来控制mFirstLayout变量。是这样的:</p>

        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            try {
                Field mFirstLayout = ViewPager.class.getDeclaredField("mFirstLayout");
                mFirstLayout.setAccessible(true);
                mFirstLayout.set(this, false);
                getAdapter().notifyDataSetChanged();
                setCurrentItem(getCurrentItem());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
于 2019-05-05T03:48:28.080 回答
0

在浪费了我一整天之后,我找到了一个解决方案,将 offscreenPageLimit 设置为 total no。的页面。

请遵循此https://stackoverflow.com/a/54800154/9097612

于 2019-02-21T06:02:35.237 回答
0

正如其他人后来发现的那样,这是一个错误,我们必须延迟发布,但它可以很简单:

pager.post {
    pager.setCurrentItem(1, true)
}

可悲的是,您必须尝试第一批错误的答案才能解决此问题...

于 2020-06-22T23:30:01.577 回答
-6

试试这个代码:

viewPager.postDelayed(new Runnable()
{
    @Override
    public void run()
    {
        viewPager.setCurrentItem(id, true);
    }
}, 100);
于 2016-09-15T21:40:50.593 回答