目标
构建循环ViewPager。
第一个元素可以让你达到峰值到最后一个元素并滑动到它,反之亦然。您应该能够永远向任一方向滑动。
现在这已经完成了,但这些问题对我的实现不起作用。以下是一些供参考:
我如何解决问题
我们将使用大小为7的数组作为示例。要素如下:
[0][1][2][3][4][5][6]
当您在元素0时,ViewPagers不允许您向左滑动!多么可怕:(。为了解决这个问题,我在前面添加了1个元素并结束。
[0][1][2][3][4][5][6] // Original
[0][1][2][3][4][5][6][7][8] // New mapping
当ViewPageAdapter请求(instantiateItem())元素0时,我们返回元素7.当ViewPageAdapter请求元素8时,我们返回元素1.
同样在ViewPager的OnPageChangeListener中,当使用0调用onPageSelected时,我们使用setCurrentItem(7),当使用8调用onPageSelected时,我们setCurrentItem(1)。
这很有效。
问题
当您从1向左滑动到0时,我们设置了CurrentItem(7),它将一直向右移动6个全屏幕。这不会给出圆形ViewPager的外观,它会让用户在滑动动作时向相反方向的最后一个元素冲去!#/ p>
这非常非常刺耳。
我如何尝试解决此问题
我的第一个倾向是关闭平滑(即所有)动画。它有点好,但是当你从最后一个元素移动到第一个元素时反之亦然,反之亦然。
然后我制作了自己的Scroller。
http://developer.android.com/reference/android/widget/Scroller.html
我发现在元素之间移动时始终有1次调用startScroll(),除了,当我从1移动到7和7移动到1时。
第一个调用是方向和数量的正确动画。
第二个调用是将所有内容向右移动多个页面的动画。
这是事情变得非常棘手的地方。
我认为解决方案是跳过第二个动画。所以我做了。会发生一个平滑的动画,从1到7,有0个打嗝。完善!但是,如果你滑动,甚至点击屏幕,你会突然(没有动画)在元素6!如果你从7滑动到1,你实际上是在元素2.没有调用setCurrentItem(2)甚至调用OnPageChangeListener,表明你在任何时间点到达2。
但是你并没有真正参加第2项,这有点好。您仍处于元素1,但将显示元素2的视图。然后当你向左滑动时,你会转到元素1.即使你已经真的在元素1上了......如何帮助清理一些代码:
动画被破坏,但没有奇怪的副作用
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
super.startScroll(startX, startY, dx, dy, duration);
}
动画有效!但是一切都很奇怪而且可怕......
@Override
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
if (dx > 480 || dx < -480) {
} else {
super.startScroll(startX, startY, dx, dy, duration);
}
}
唯一的区别是,当第二个动画(大于480像素屏幕的宽度)被调用时,我们会忽略它。
在阅读了Scroller的Android源代码后,我发现startScroll没有开始滚动任何内容。它设置了所有要滚动的数据,但没有启动任何内容。
我的预感
执行循环操作(1到7或7到1)时,会有两次调用startScroll()。我认为这两个电话之间的某些问题导致了一个问题。
如果我评论4,那么5变为&#34;短的正确动画到左边,事情变得疯狂&#34;
摘要
我实现了一个循环ViewPager,但是动画被破坏了。在尝试修复动画时,它会破坏ViewPager的功能。我正在旋转我的车轮试图找出如何使它工作。帮我! :)
如果有什么不清楚请在下面评论,我会澄清。我意识到我对事情的破坏并不十分精确。这很难描述,因为它甚至不清楚我在屏幕上看到了什么。如果我的解释是一个问题我可以解决它,请告诉我!
干杯, Coltin
代码
稍微修改了这段代码,使其自身更具可读性,但功能与我当前的代码迭代相同。
OnPageChangeListener.onPageSelected
@Override
public void onPageSelected(int _position) {
boolean animate = true;
if (_position < 1) {
// Swiping left past the first element, go to element (9 - 2)=7
setCurrentItem(getAdapter().getCount() - 2, animate);
} else if (_position >= getAdapter().getCount() - 1) {
// Swiping right past the last element
setCurrentItem(1, animate);
}
}
CircularScroller.startScroll
@Override
public void startScroll(int _startX, int _startY, int _dx, int _dy, int _duration) {
// 480 is the width of the screen
if (dx > 480 || dx < -480) {
// Doing nothing in this block shows the correct animation,
// but it causes the issues mentioned above
// Uncomment to do the big scroll!
// super.startScroll(_startX, _startY, _dx, _dy, _duration);
// lastDX was to attempt to reset the scroll to be the previous
// correct scroll distance; it had no effect
// super.startScroll(_startX, _startY, lastDx, _dy, _duration);
} else {
lastDx = _dx;
super.startScroll(_startX, _startY, _dx, _dy, _duration);
}
}
CircularViewPageAdapter.CircularViewPageAdapter
private static final int m_Length = 7; // For our example only
private static Context m_Context;
private boolean[] created = null; // Not the best practice..
public CircularViewPageAdapter(Context _context) {
m_Context = _context;
created = new boolean[m_Length];
for (int i = 0; i < m_Length; i++) {
// So that we do not create things multiple times
// I thought this was causing my issues, but it was not
created[i] = false;
}
}
CircularViewPageAdapter.getCount
@Override
public int getCount() {
return m_Length + 2;
}
CircularViewPageAdapter.instantiateItem
@Override
public Object instantiateItem(View _collection, int _position) {
int virtualPosition = getVirtualPosition(_position);
if (created[virtualPosition - 1]) {
return null;
}
TextView tv = new TextView(m_Context);
// The first view is element 1 with label 0! :)
tv.setText("Bonjour, merci! " + (virtualPosition - 1));
tv.setTextColor(Color.WHITE);
tv.setTextSize(30);
((ViewPager) _collection).addView(tv, 0);
return tv;
}
CircularViewPageAdapter.destroyItem
@Override
public void destroyItem(ViewGroup container, int position, Object view) {
ViewPager viewPager = (ViewPager) container;
// If the virtual distance is distance 2 away, it should be destroyed.
// If it's not intuitive why this is the case, please comment below
// and I will clarify
int virtualDistance = getVirtualDistance(viewPager.getCurrentItem(), getVirtualPosition(position));
if ((virtualDistance == 2) || ((m_Length - virtualDistance) == 2)) {
((ViewPager) container).removeView((View) view);
created[getVirtualPosition(position) - 1] = false;
}
}
答案 0 :(得分:1)
我认为最好的可行方法是使用普通列表来为List创建一个包装器,当执行 get(pos)方法获取创建视图的对象时,你做了类似 get(pos%numberOfViews)之类的东西,当它要求你输入List的大小 Integer.MAX_VALUE 时,你就开始列表了在它的中间所以你可以说几乎不可能有一个错误,除非他们实际滑到同一侧,直到达到列表的结尾。如果时间允许的话,我会尝试在后面发布一个概念验证。
编辑:
我已经尝试过这段代码,我知道每个视图上都显示了一个简单的文本框,但事实是它完美无缺,根据视图的总量可能会更慢,但概念验证就在这里。我所做的是 MAX_NUMBER_VIEWS 表示用户在停止之前可以完全给出的最大次数。正如你所看到的那样,我在数组的长度上启动了viewpager,这样它就会第二次出现,所以你左右一转,但你可以根据需要改变它。我希望我不会因为一个实际上有效的解决方案而得到更多的负面影响。
<强>活性强>
pager = (ViewPager)findViewById(R.id.viewpager);
String[] articles = {"ARTICLE 1","ARTICLE 2","ARTICLE 3","ARTICLE 4"};
pager.setAdapter(new ViewPagerAdapter(this, articles));
pager.setCurrentItem(articles.length);
<强> ADAPTER:强>
public class ViewPagerAdapter extends PagerAdapter {
private Context ctx;
private String[] articles;
private final int MAX_NUMBER_VIEWS = 3;
public ViewPagerAdapter(Context ctx, String[] articles) {
this.ctx = ctx;
this.articles = articles.clone();
}
@Override
public int getCount() {
return articles.length * this.MAX_NUMBER_VIEWS;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
TextView view = new TextView(ctx);
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT));
int realPosition = position % articles.length;
view.setText(this.articles[realPosition]);
((ViewPager) container).addView(view);
return view;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
((ViewPager) container).removeView((View) object);
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == ((View) object);
}
@Override
public Parcelable saveState() {
return null;
}
}