如何创build一个封闭的(循环)ListView?

我想要创build一个自定义的ListView(或类似的),它将performance得像一个封闭的(圆形的):

  1. 向下滚动 – 在最后一个项目到达后,第一个开始(..,n-1,n,1,2,..)
  2. 向上滚动 – 在第一个项目到达后,最后一个开始(..,2,1,n,n-1,..)

这在概念上听起来很简单,但显然,没有直接的方法来做到这一点。 任何人都可以指出我正确的解决scheme? 谢谢 !

我已经收到了一个答案(来自波士顿街道上的Android开发人员谷歌组),但它听起来不知何故难看:) –

我通过创build我自己的列表适配器(从BaseAdapter分类)做到了这一点。

我编写我自己的列表适配器,使得它的getCount()方法返回一个HUUUUGE数字。

如果项目'x'被选中,则这个项目对应于适配器位置='adapter.getCount()/ 2 + x'

对于我的适配器的方法getItem(int position),我查看我的数组,备份适配器并获取索引上的项目:(position-getCount()/ 2)%myDataItems.length

你需要做一些更“特殊”的东西,使其一切工作正常,但你明白了。

原则上,它仍然可以达到列表的结尾或开始,但是如果将getCount()设置为大约一百万左右,这很难做到:-)

我的同事乔,我相信我们已经find了一个更简单的方法来解决同样的问题。 在我们的解决scheme中,我们扩展了ArrayAdapter,而不是扩展BaseAdapter。

代码如下:

 public class CircularArrayAdapter extends ArrayAdapter { public static final int HALF_MAX_VALUE = Integer.MAX_VALUE/2; public final int MIDDLE; private T[] objects; public CircularArrayAdapter(Context context, int textViewResourceId, T[] objects) { super(context, textViewResourceId, objects); this.objects = objects; MIDDLE = HALF_MAX_VALUE - HALF_MAX_VALUE % objects.length; } @Override public int getCount() { return Integer.MAX_VALUE; } @Override public T getItem(int position) { return objects[position % objects.length]; } } 

所以这创build了一个名为CircularArrayAdapter的类,它接受一个对象typesT(可以是任何东西)并用它来创build一个数组列表。 T通常是一个string,尽pipe可能是任何东西。

虽然初始化一个叫做middle的常量,但构造函数与ArrayAdapter的相同。 这是名单的中间。 不pipe数组MIDDLE的长度是什么,都可以用来将ListView居中放在列表中间。

getCount被覆盖以返回一个巨大的值,就像上面创build一个巨大的列表一样。

getItem被覆盖以返回数组上的假位置。 因此,填充列表时,列表以循环方式填充对象。

此时,CircularArrayAdapter简单地replace创buildListView的文件中的ArrayAdapter。

要将ListView居中,必须在ListView对象初始化后,将下面的行插入到创buildListView的文件中:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

并使用之前为列表初始化的MIDDLE常量,视图以屏幕顶部列表顶部的项目为中心。

:)〜干杯,我希望这个解决scheme很有用。

你提到的解决scheme是我曾经告诉其他开发人员使用的解决scheme。 在getCount()中,只需返回Integer.MAX_VALUE,它会给你大约20亿个项目,这应该足够了。

根据上面的答案,我有,或者我认为我做得对。 希望这会帮助你。

 private static class RecipeListAdapter extends BaseAdapter { private static LayoutInflater mInflater; private Integer[] mCouponImages; private static ImageView viewHolder; public RecipeListAdapter(Context c, Integer[] coupomImages) { RecipeListAdapter.mInflater = LayoutInflater.from(c); this.mCouponImages = coupomImages; } @Override public int getCount() { return Integer.MAX_VALUE; } @Override public Object getItem(int position) { // you can do your own tricks here. to let it display the right item in your array. return position % mCouponImages.length; } @Override public long getItemId(int position) { return position; // return position % mCouponImages.length; } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mInflater.inflate(R.layout.coupon_list_item, null); viewHolder = (ImageView) convertView.findViewById(R.id.item_coupon); convertView.setTag(viewHolder); } else { viewHolder = (ImageView) convertView.getTag(); } viewHolder.setImageResource(this.mCouponImages[position % mCouponImages.length]); return convertView; } } 

如果你想向下滚动列表,你想这样做。 通常我们可以向上滚动并列出,然后向下滚动。

//看看我们想要打几个项目 在这种情况下,Integer.MAX_VALUE

 int listViewLength = adapter.getCount(); // see how many items a screen can dispaly, I use variable "span" final int span = recipeListView.getLastVisiblePosition() - recipeListView.getFirstVisiblePosition(); 

//看看我们有多less页

 int howManySpans = listViewLength / span; 

//开始列表视图时,看看你想要在哪里。 你不必做“-3”的东西。 这是我的应用程序正确工作。

 recipeListView.setSelection((span * (howManySpans / 2)) - 3); 

我可以看到一些很好的答案,我的一个朋友试图通过一个简单的解决scheme来实现这一点。 检查github项目。

如果使用LoadersCallbacks,我创build了MyCircularCursor类,它包装了典型的光标,如下所示:

 @Override public void onLoadFinished(Loader<Cursor> pCursorLoader, Cursor pCursor) { mItemListAdapter.swapCursor(new MyCircularCursor(pCursor)); } 

装饰类的代码在这里:

 public class MyCircularCursor implements Cursor { private Cursor mCursor; public MyCircularCursor(Cursor pCursor) { mCursor = pCursor; } @Override public int getCount() { return mCursor.getCount() == 0 ? 0 : Integer.MAX_VALUE; } @Override public int getPosition() { return mCursor.getPosition(); } @Override public boolean move(int pOffset) { return mCursor.move(pOffset); } @Override public boolean moveToPosition(int pPosition) { int position = MathUtils.mod(pPosition, mCursor.getCount()); return mCursor.moveToPosition(position); } @Override public boolean moveToFirst() { return mCursor.moveToFirst(); } @Override public boolean moveToLast() { return mCursor.moveToLast(); } @Override public boolean moveToNext() { if (mCursor.isLast()) { mCursor.moveToFirst(); return true; } else { return mCursor.moveToNext(); } } @Override public boolean moveToPrevious() { if (mCursor.isFirst()) { mCursor.moveToLast(); return true; } else { return mCursor.moveToPrevious(); } } @Override public boolean isFirst() { return false; } @Override public boolean isLast() { return false; } @Override public boolean isBeforeFirst() { return false; } @Override public boolean isAfterLast() { return false; } @Override public int getColumnIndex(String pColumnName) { return mCursor.getColumnIndex(pColumnName); } @Override public int getColumnIndexOrThrow(String pColumnName) throws IllegalArgumentException { return mCursor.getColumnIndexOrThrow(pColumnName); } @Override public String getColumnName(int pColumnIndex) { return mCursor.getColumnName(pColumnIndex); } @Override public String[] getColumnNames() { return mCursor.getColumnNames(); } @Override public int getColumnCount() { return mCursor.getColumnCount(); } @Override public byte[] getBlob(int pColumnIndex) { return mCursor.getBlob(pColumnIndex); } @Override public String getString(int pColumnIndex) { return mCursor.getString(pColumnIndex); } @Override public short getShort(int pColumnIndex) { return mCursor.getShort(pColumnIndex); } @Override public int getInt(int pColumnIndex) { return mCursor.getInt(pColumnIndex); } @Override public long getLong(int pColumnIndex) { return mCursor.getLong(pColumnIndex); } @Override public float getFloat(int pColumnIndex) { return mCursor.getFloat(pColumnIndex); } @Override public double getDouble(int pColumnIndex) { return mCursor.getDouble(pColumnIndex); } @Override public int getType(int pColumnIndex) { return 0; } @Override public boolean isNull(int pColumnIndex) { return mCursor.isNull(pColumnIndex); } @Override public void deactivate() { mCursor.deactivate(); } @Override @Deprecated public boolean requery() { return mCursor.requery(); } @Override public void close() { mCursor.close(); } @Override public boolean isClosed() { return mCursor.isClosed(); } @Override public void registerContentObserver(ContentObserver pObserver) { mCursor.registerContentObserver(pObserver); } @Override public void unregisterContentObserver(ContentObserver pObserver) { mCursor.unregisterContentObserver(pObserver); } @Override public void registerDataSetObserver(DataSetObserver pObserver) { mCursor.registerDataSetObserver(pObserver); } @Override public void unregisterDataSetObserver(DataSetObserver pObserver) { mCursor.unregisterDataSetObserver(pObserver); } @Override public void setNotificationUri(ContentResolver pCr, Uri pUri) { mCursor.setNotificationUri(pCr, pUri); } @Override public boolean getWantsAllOnMoveCalls() { return mCursor.getWantsAllOnMoveCalls(); } @Override public Bundle getExtras() { return mCursor.getExtras(); } @Override public Bundle respond(Bundle pExtras) { return mCursor.respond(pExtras); } @Override public void copyStringToBuffer(int pColumnIndex, CharArrayBuffer pBuffer) { mCursor.copyStringToBuffer(pColumnIndex, pBuffer); } }