Android ViewPager setCurrentItem(pageId,true)无法平滑滚动

Android ViewPager setCurrentItem(pageId,true)无法平滑滚动,android,android-layout,Android,Android Layout,我在SDK 4.03、Samsung Infuse Android 2.2、Android 4支持库上编译,并在我的应用程序中使用ViewPager,实际的滑动效果很好,但当我这样做的时候 viewPager.setCurrentItem(id); // , or viewPager.setCurrentItem(id, true); 它不会平滑滚动,但会立即切换视图。尽管文档清楚地说明了这是将第二个参数设置为true的目的。这是怎么回事?ViewPager从第5版更改为第9版。其中一些更

我在SDK 4.03、Samsung Infuse Android 2.2、Android 4支持库上编译,并在我的应用程序中使用ViewPager,实际的滑动效果很好,但当我这样做的时候

viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);  
它不会平滑滚动,但会立即切换视图。尽管文档清楚地说明了这是将第二个参数设置为true的目的。这是怎么回事?

ViewPager从第5版更改为第9版。其中一些更改可能与您的问题有关:

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

考虑到您的代码应该工作正常,我最好的猜测是您的支持库不是最新的。如果没有,请尝试更新库。

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

@Override
public void onClick(View v) {
   viewPager.setCurrentItem(item, true);
}
之后,平滑的卷轴开始工作。因此,解决方案非常简单:在Button类中,内部布尔onTouch(…)侦听器返回true,因此它总是使用触摸事件!然后,当我在内部布尔onTouch(…)侦听器中将“return false”替换为“return true”时,平滑滚动开始用于我的自定义选项卡视图


我希望我的成功故事能对你有所帮助。

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

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

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

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

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*/);
        }
    }
}
公共类MyViewPager扩展了ViewPager{
公共MyViewPager(上下文、属性集属性){
超级(上下文,attrs);
setMyScroller();
}
私有void setMyScroller(){
试一试{
Class viewpager=viewpager.Class;
字段滚动器=viewpager.getDeclaredField(“mScroller”);
scroller.setAccessible(true);
set(这是新的MyScroller(getContext());
}捕获(例外e){
e、 printStackTrace();
}
}
公共类MyScroller扩展了Scroller{
公共MyScroller(上下文){
super(上下文,新减速器插值器());
}
@凌驾
公共无效开始滚动(int startX、int startY、int dx、int dy、int duration){
超级startScroll(startX,startY,dx,dy,1000/*1秒*/);
}
}
}
对于那些使用Xamarin的人(尽管这种方法也适用于Java),我可以根据上面的答案建议使用下一种方法(来自Android支持库v7 AppCompat 19.1.0的ViewPager):


它依赖于来自

的ViewPager实现,我知道这个线程很旧,但这是谷歌的顶级结果之一。我已经反复讨论如何解决这个问题很久了。以上所有的解决方案都对我毫无帮助。然而,我确实找到了一个适合我的解决方案

我的设置当前看起来像是viewpager中的listview。当您单击其中一个视图时,它将创建一个新页面并滚动到该页面。这是非常快之前,但似乎这是因为我打电话

mViewPager.setCurrentItem(intIndex, true);
从我的事件内部。由于某些原因,viewpager不喜欢此功能,因此我创建了此功能。它创建一个新线程,在UI线程上运行runnable。此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();


}
publicstaticvoidscrolltoindex(int索引){
最终int intIndex=索引;
//我们要做另一条线,把我们自己和任何东西分开
//我们在哪
Thread sepThread=新线程(new Runnable(){
公开募捐
{
//然后,在该线程内,在ui线程上运行runnable。
//getContext()是一个静态函数,返回
//活动的上下文。它在必要时很有用。
((活动)MyActivity.getContext()).runOnUiThread(新的Runnable()){
@凌驾
公开募捐{
if(mSlidingTabLayout!=null)
{
//我也用了一个标签带,确保
//tabstrip已更新,否则将无法滚动
//正确地

if(mSlidingTabLayout.getTabStripChildCount()我在处理程序中调用了
setCurrentItem
函数,它对我来说运行良好

new Handler().post(new Runnable() {
        @Override
        public void run() {
            myViewPager.setCurrentItem(1, true);
        }
    });
希望这有帮助。

尝试使用以下代码:

viewPager.postDelayed(new Runnable()
{
    @Override
    public void run()
    {
        viewPager.setCurrentItem(id, true);
    }
}, 100);

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

该类重写
setCurrentItem(int-item,boolean smoothScroll)
,并使用反射尽可能保持此方法的原始性。关键是速度不是0。 您只需将当前的
ViewPager
替换为该
MyViewPager
类,并像正常情况一样使用它:

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();
        }
    }
}
导入android.content.Context;
导入android.util.AttributeSet;
导入java.lang.reflect.Field;
导入java.lang.reflect.Method;
导入androidx.viewpager.widget.viewpager;
公共类MyViewPager扩展了ViewPager{
公共MyViewPager(上下文、属性集属性){
超级(上下文,attrs);
}
/**
*设置当前选定的pag
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();
        }
    }
}
        @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();
            }
        }
import androidx.viewpager.widget.ViewPager;


new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            viewPager.setCurrentItem(index, true);
        }
    }, 500);
pager.post {
    pager.setCurrentItem(1, true)
}