我正在 SDK 4.03、Samsung Infuse Android 2.2、Android 4 支持库上编译,并在我的应用程序中使用 ViewPager,实际滑动工作正常,但是当我这样做时
viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);
它不会平滑滚动,但会立即切换视图。尽管文档明确指出,这是将第二个参数设置为 true 的目的。这是怎么回事?
我正在 SDK 4.03、Samsung Infuse Android 2.2、Android 4 支持库上编译,并在我的应用程序中使用 ViewPager,实际滑动工作正常,但是当我这样做时
viewPager.setCurrentItem(id); // , or
viewPager.setCurrentItem(id, true);
它不会平滑滚动,但会立即切换视图。尽管文档明确指出,这是将第二个参数设置为 true 的目的。这是怎么回事?
我通过创建一个使用反射覆盖 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*/);
}
}
}
这就是我所做的。我通过将我自己的自定义子类放在同一个包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。
我setCurrentItem
在处理程序中调用了函数,它对我来说很好。
new Handler().post(new Runnable() {
@Override
public void run() {
myViewPager.setCurrentItem(1, true);
}
});
希望这可以帮助。
我知道这个帖子很老了,但这是谷歌搜索结果中的佼佼者之一。我一直在反复讨论如何解决这个问题。上述解决方案都没有帮助我。但是,我确实找到了适合我的解决方案。
我的设置目前看起来是 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 线程上运行,它应该可以工作。不过,我还没有测试过。
我创建了这个类,因为我对上述解决方案并不完全满意
该类覆盖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();
}
}
}
对于那些使用 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));
}
ViewPager 从修订版 5 到修订版 9 发生了很大变化。其中一些更改可能与您的问题有关:
鉴于您的代码应该可以正常工作,我最好的猜测是您的支持库不是最新的。如果不是,请尝试更新库。
我有同样的问题,但今天我找到了一个简单的解决方案。也许它会帮助你。首先,假设我们有一个填满整个屏幕的 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”时,平滑滚动开始为我的自定义选项卡视图工作。
我希望我的成功故事可以帮助到你。
下面的代码将暂停页面片刻(500 毫秒)然后滑动到下一个。
import androidx.viewpager.widget.ViewPager;
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
viewPager.setCurrentItem(index, true);
}
}, 500);
注意这个变量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();
}
}
在浪费了我一整天之后,我找到了一个解决方案,将 offscreenPageLimit 设置为 total no。的页面。
正如其他人后来发现的那样,这是一个错误,我们必须延迟发布,但它可以很简单:
pager.post {
pager.setCurrentItem(1, true)
}
可悲的是,您必须尝试第一批错误的答案才能解决此问题...
试试这个代码:
viewPager.postDelayed(new Runnable()
{
@Override
public void run()
{
viewPager.setCurrentItem(id, true);
}
}, 100);