¿Cómo crear un ListView cerrado (circular)?

Quiero crear un ListView personalizado (o similar) que se comportará como uno cerrado (circular):

  1. desplazarse hacia abajo – después de que se alcanzó el último elemento, comienza el primero (…, n-1, n, 1, 2, …)
  2. desplazarse hacia arriba – después de que se alcanzó el primer elemento, comienza el último (…, 2, 1, n, n-1, …)

Suena simple conceptualmente pero, aparentemente, no hay un enfoque directo para hacer esto. ¿Alguien puede señalarme la solución correcta? Gracias !

Ya recibí una respuesta (de Streets Of Boston en Android-Developers google groups), pero suena algo feo 🙂 –

Hice esto creando mi propio adaptador de lista (subclasificado de BaseAdapter).

Codifiqué mi propio adaptador de lista de tal manera que su método getCount () devuelve un número HUUUUGE.

Y si se selecciona el elemento ‘x’, este elemento corresponde a la posición del adaptador = ‘adapter.getCount () / 2 + x’

Y para el método getItem (int position) de mi adaptador, busco en mi matriz que haga una copia de seguridad del adaptador y busque el elemento en el índice: (position-getCount () / 2)% myDataItems.length

Debes hacer algunas cosas más “especiales” para que todo funcione correctamente, pero entiendes la idea.

En principio, todavía es posible llegar al final o al principio de la lista, pero si configura getCount () en alrededor de un millón o así, esto es difícil de hacer 🙂

Mi colega Joe, y creo que hemos encontrado una manera más simple de resolver el mismo problema. En nuestra solución, aunque en lugar de extender BaseAdapter, ampliamos ArrayAdapter.

El código es como barbechos:

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

Así que esto crea una clase llamada CircularArrayAdapter que toma un tipo de objeto T (que puede ser cualquier cosa) y lo usa para crear una lista de matriz. T es comúnmente una cuerda, aunque puede ser cualquier cosa.

El constructor es el mismo que para ArrayAdapter aunque inicializa una constante llamada middle. Este es el medio de la lista. No importa la longitud de la matriz MIDDLE, se puede usar para centrar ListView en la mitad de la lista.

getCount se sobrescribe para devolver un gran valor como se hizo anteriormente creando una lista enorme.

getItem está anulado para devolver la posición falsa en la matriz. Por lo tanto, al completar la lista, la lista se llena con objetos de forma cíclica.

En este punto, CircularArrayAdapter simplemente reemplaza ArrayAdapter en el archivo que crea el ListView.

Para centrar el ListView, debe insertarse la siguiente línea en su archivo creando el ListView después de que el objeto ListView se haya inicializado:

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

y utilizando la constante MIDDLE previamente inicializada para la lista, la vista se centra con el elemento superior de la lista en la parte superior de la pantalla.

🙂 ~ Saludos, espero que esta solución sea útil.

La solución que mencionas es la que les dije a otros desarrolladores que usaran en el pasado. En getCount (), simplemente devuelve Integer.MAX_VALUE, le dará aproximadamente 2 mil millones de elementos, lo cual debería ser suficiente.

Tengo, o creo que lo he hecho bien, en base a las respuestas anteriores. Espero que esto te ayudará.

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

Y le gustaría hacer esto si quiere desplazarse hacia abajo en la lista. Comúnmente podemos simplemente desplazarnos hacia arriba y hacia la lista y luego hacia abajo.

// ver cuántos elementos nos gustaría ejecutar. en este caso, 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(); 

// mira cuántas páginas tenemos

 int howManySpans = listViewLength / span; 

// mira dónde quieres estar cuando inicies la vista de lista. no tienes que hacer las cosas “-3”. es para que mi aplicación funcione correctamente.

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

Pude ver algunas buenas respuestas para esto, uno de mis amigos ha tratado de lograr esto a través de una solución simple. Verifica el proyecto github .

Si utilizo LoadersCallbacks, he creado la clase MyCircularCursor que ajusta el cursor típico de esta manera:

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

el código de clase del decorador está aquí:

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