我正在使用SDK 4.03、三星Infuse Android 2.2和Android 4支持库进行编译,并在我的应用程序中使用ViewPager,实际的滑动效果很好,但当我执行
viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);
它不会平稳滚动,而是立即切换视图。尽管文档清楚地说明了将第二个参数设置为true的目的,但是这是怎么回事?
我正在使用SDK 4.03、三星Infuse Android 2.2和Android 4支持库进行编译,并在我的应用程序中使用ViewPager,实际的滑动效果很好,但当我执行
viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);
它不会平稳滚动,而是立即切换视图。尽管文档清楚地说明了将第二个参数设置为true的目的,但是这是怎么回事?
我通过创建一个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*/);
}
}
}
我在一个处理程序中调用了setCurrentItem
函数,对我来说它运行得很好。
new Handler().post(new Runnable() {
@Override
public void run() {
myViewPager.setCurrentItem(1, 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。
viewPager.setCurrentItem(id, true);
,请使用此检查。 - tegbirdandroid/support/v4/
而不是com/example/app/
中,并且在你的类的包行中写上package android.support.v4;
。 - Mikael OhlsonmViewPager.setCurrentItem(intIndex, true);
从我的OnClickEvent内部调用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();
}
我希望这篇文章能够帮到你。祝你好运!
编辑:重新审视我的回答,我相信你只需要在UI线程上运行它,它应该可以工作。但请注意,我还没有测试过。
我创建了这个类,因为我对上述解决方案并不完全满意。
该类重写了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();
}
}
}
后来其他人发现,这是一个bug,我们必须延迟发布它,但解决方法可能非常简单:
pager.post {
pager.setCurrentItem(1, true)
}
不幸的是,您必须先尝试一堆错误的答案才能到达这个...
mFirstLayout
- 当viewpager回调onAttachedToWindow
(例如在recyclerview上),它将被设置为true,因此不会平滑滚动。您应该重写onAttachedToWindow来控制mFirstLayout
变量。类似于这样的代码:
@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();
}
}
对于那些使用Xamarin的人(尽管这种方法也适用于Java),我建议使用上面答案中基于Android Support Library v7 AppCompat 19.1.0的下一步方法:
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实现。
ViewPager从版本5到9进行了大量的更改。以下是一些可能与您的问题相关的更改:
考虑到您的代码应该可以正常工作,我最好的猜测是您的支持库不是最新的。如果不是,请尝试更新库。
在浪费了整个一天的时间后,我找到了一个解决方案,将 offscreenPageLimit 设置为页面的总数。
Android Compatibility Library 4
中的 support library 吗? - iTurki