Wie erstelle ich einen geschlossenen (kreisförmigen) ListView?

Ich möchte ein benutzerdefiniertes ListView (oder ähnliches) erstellen, das sich wie ein geschlossenes (zirkuläres) verhält:

  1. Scrollen nach unten – nachdem der letzte Gegenstand erreicht wurde, beginnt der erste (.., n-1, n, 1, 2, ..)
  2. nach oben scrollen – nachdem der erste Gegenstand erreicht wurde, beginnt der letzte (.., 2, 1, n, n-1, ..)

Es klingt einfach konzeptionell, aber anscheinend gibt es keinen direkten Ansatz dafür. Kann mir jemand auf die richtige Lösung hinweisen? Vielen Dank !

Ich habe bereits eine Antwort erhalten (von Streets Of Boston auf Android-Entwicklern Google-Gruppen), aber es klingt irgendwie hässlich 🙂 –

Ich habe das gemacht, indem ich meinen eigenen Listen-Adapter erstellt habe (abgeleitet von BaseAdapter).

Ich habe meinen eigenen Listen-Adapter so codiert, dass die Methode getCount () eine HUUUUGE-Nummer zurückgibt.

Und wenn das Element ‘x’ ausgewählt ist, entspricht dieses Element der Adapterposition = ‘adapter.getCount () / 2 + x’

Und für die Methode getItem (int position) meines Adapters schaue ich in mein Array, das den Adapter sichert und das Objekt im Index holt: (position-getCount () / 2)% myDataItems.length

Sie müssen noch ein paar “spezielle” Dinge tun, damit alles richtig funktioniert, aber Sie bekommen die Idee.

Im Prinzip ist es immer noch möglich, das Ende oder den Anfang der Liste zu erreichen, aber wenn Sie getCount () auf etwa eine Million setzen, ist das schwer zu tun 🙂

Mein Kollege Joe, und ich glaube, wir haben einen einfacheren Weg gefunden, das gleiche Problem zu lösen. In unserer Lösung erweitern wir jedoch, anstatt BaseAdapter zu erweitern, ArrayAdapter.

Der Code ist wie eine Brache:

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

Das erzeugt also eine class namens CircularArrayAdapter, die einen Objekttyp T (der alles Mögliche sein kann) verwendet und damit eine Array-Liste erstellt. T ist normalerweise eine Zeichenkette, kann aber auch nichts sein.

Der Konstruktor ist derselbe wie für ArrayAdapter, obwohl er eine Konstante namens Mitte initialisiert. Dies ist die Mitte der Liste. Unabhängig von der Länge des Arrays MIDDLE kann das ListView in der Mitte der Liste zentriert werden.

getCount wird überschrieben, um einen großen Wert zurückzugeben, wie dies oberhalb der Erstellung einer großen Liste getan wird.

getItem wird überschrieben, um die falsche Position auf dem Array zurückzugeben. Daher wird die Liste beim Füllen der Liste mit Objekten in einer Schleife gefüllt.

An dieser Stelle ersetzt CircularArrayAdapter einfach ArrayAdapter in der Datei, die die ListView erstellt.

Um die ListView zu zentrieren, muss nach dem Initialisieren des ListView-Objekts die gerade Linie in Ihre Datei eingefügt werden.

listViewObject.setSelectionFromTop(nameOfAdapterObject.MIDDLE, 0);

Mit der MIDDLE-Konstante, die zuvor für die Liste initialisiert wurde, wird die Ansicht mit dem obersten Element der Liste am oberen Bildschirmrand zentriert.

🙂 ~ Cheers, ich hoffe diese Lösung ist nützlich.

Die Lösung, die Sie erwähnen, ist diejenige, die ich anderen Entwicklern in der Vergangenheit gesagt habe. Geben Sie in getCount () einfach Integer.MAX_VALUE zurück. Dadurch erhalten Sie ungefähr 2 Milliarden Elemente, die ausreichen sollten.

Ich habe, oder ich denke, dass ich es richtig gemacht habe, basierend auf den obigen Antworten. Hoffe das wird dir helfen.

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

Und Sie möchten dies tun, wenn Sie in der Liste nach unten scrollen möchten. Normalerweise können wir einfach hochscrollen und auflisten und dann runterscrollen.

// Sehen Sie, wie viele Objekte wir abrollen möchten. In diesem Fall 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(); 

// sehen, wie viele Seiten wir haben

 int howManySpans = listViewLength / span; 

// sehen Sie, wo Sie sein möchten, wenn Sie die Listenansicht starten. Sie müssen nicht die “-3” Sachen machen. Es ist für meine App richtig zu arbeiten.

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

Ich konnte einige gute Antworten dafür sehen. Einer meiner Freunde hat versucht, dies über eine einfache Lösung zu erreichen. Überprüfen Sie das GitHub- Projekt.

Wenn ich LoadersCallbacks verwende, habe ich die MyCircularCursor-class erstellt, die den typischen Cursor wie folgt umschließt:

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

Der Dekorator classncode ist hier:

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