Wie erstelle ich eine geschlossene (kreisförmige) ListView?
Lesezeit: 9 Minuten
Benutzer281076
Ich möchte eine benutzerdefinierte ListView (oder ähnliches) erstellen, die sich wie eine geschlossene (kreisförmige) verhält:
nach unten scrollen – nachdem das letzte Element erreicht wurde, beginnt das erste (.., n-1, n, 1, 2, ..)
nach oben scrollen – nachdem das erste Element erreicht wurde, beginnt das letzte (.., 2, 1, n, n-1, ..)
Es klingt konzeptionell einfach, aber anscheinend gibt es dafür keinen direkten Ansatz. Kann mir jemand die richtige Lösung zeigen? Danke !
Ich habe bereits eine Antwort erhalten (von Streets Of Boston auf Android-Developers Google Groups), aber es klingt irgendwie hässlich 🙂 –
Ich habe dies getan, indem ich meinen eigenen Listenadapter erstellt habe (von BaseAdapter abgeleitet).
Ich habe meinen eigenen Listenadapter so codiert, dass seine Methode getCount() eine RIESIGE Zahl zurückliefert.
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 hole das Element auf dem Index: (position-getCount()/2) % myDataItems.length
Sie müssen noch einige “spezielle” Dinge tun, damit alles richtig funktioniert, aber Sie verstehen 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 oder so setzen, ist dies schwierig 🙂
Die Lösung von Herrn Boston ist die einzige verfügbare Option, wenn Sie sich an den Unterricht halten AdapterView Hierarchie (z. B. ListView).
– CommonsWare
25. Februar 2010 um 13:01 Uhr
Ich halte mich nicht an irgendeinen Unterricht. Ich erwähne ListView nur, um eine Vorstellung vom Verhalten und Aussehen des Steuerelements zu geben, Sie können es auch “Tabelle” nennen, wenn Sie möchten. Etwas sehr Benutzerdefiniertes könnte eine Reihe von Zellen sein, die eine Liste bilden, und wenn eine dieser Zellen den sichtbaren Bereich verlässt, aktualisiert die Engine (von einem anderen Thread, glaube ich) ihre Position (Koordinaten) und ihren Inhalt (Text + Bilder). . Dieser Aktualisierungsvorgang kann jedoch das reibungslose Scrollen beeinträchtigen.
– Benutzer281076
25. Februar 2010 um 14:49 Uhr
Wo soll ich position=adapter.getcount()/2+x schreiben?
– Schwierig
26. September 2014 um 11:25 Uhr
Dawson
Mein Kollege Joe und ich glauben, dass wir einen einfacheren Weg gefunden haben, dasselbe Problem zu lösen. In unserer Lösung erweitern wir jedoch, anstatt BaseAdapter zu erweitern, ArrayAdapter.
Der Code lautet wie folgt:
public class CircularArrayAdapter< T > extends ArrayAdapter< T >
{
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];
}
}
Dies erstellt also eine Klasse namens CircularArrayAdapter, die einen Objekttyp T (der alles sein kann) nimmt und ihn zum Erstellen einer Array-Liste verwendet. T ist normalerweise eine Zeichenfolge, kann aber alles sein.
Der Konstruktor ist derselbe wie für ArrayAdapter, initialisiert jedoch eine Konstante namens middle. Dies ist die Mitte der Liste. Unabhängig von der Länge des Arrays MIDDLE kann die ListView in der Mitte der Liste zentriert werden.
getCount() Dies überschreibt, um einen riesigen Wert zurückzugeben, wie es oben beim Erstellen einer riesigen Liste getan wurde.
getItem() wird überschrieben, um die gefälschte Position auf dem Array zurückzugeben. Somit wird beim Füllen der Liste die Liste in einer Schleifenweise mit Objekten gefüllt.
An diesem Punkt ersetzt CircularArrayAdapter einfach ArrayAdapter in der Datei, die die ListView erstellt.
Um die ListView zu zentrieren, muss die folgende Zeile in Ihre Datei eingefügt werden, die die ListView erstellt, nachdem das ListView-Objekt initialisiert wurde:
und unter Verwendung der zuvor für die Liste initialisierten MIDDLE-Konstante wird die Ansicht mit dem obersten Element der Liste am oberen Rand des Bildschirms zentriert.
🙂 ~ Prost, ich hoffe, diese Lösung ist nützlich.
Das sollte die Lösung sein. Das ist verblüffend einfach und funktioniert einwandfrei. Danke
– Tom
1. Juni 2011 um 0:27 Uhr
@Dawson: Ob die Elemente aktualisiert werden, während sie ganz oben sind (dh) von unten nach oben scrollen. Nach dem erneuten Erreichen des ersten Elements werden die letzten Elemente oben angezeigt?
– Surej
24. April 2012 um 6:19 Uhr
Ich bin gerade auf diese Lösung gestoßen, und ehrlich gesagt ist es erstaunlich, wie einfach der Code ist. Hut ab!
– Sreedevi J
29. August 2013 um 6:52 Uhr
@prodaea Sie verwalten sich selbst. Wenn ich 19 Elemente im Array habe und zum Ende der Liste scrolle, also mein Index 0 ist, ist alles in Ordnung. Wenn ich ein zusätzliches Element nach unten scrolle und mein Index somit -1 ist, wird -1 % 19 in Java zu 18 ausgewertet, was das letzte Element in meiner Liste ist. In einer kreisförmigen Liste ist das letzte Element logisch vor dem ersten Element, und damit sind wir in Ordnung. In Anbetracht dessen sollte MIDDLE auf 0 gesetzt werden, was es zum Mittelpunkt zwischen Integer.MIN_VALUE und Integer.MAX_VALUE macht. Ich bin froh, dass Sie dies angesprochen haben, weil ich diesen Fall nicht berücksichtigt hatte.
– Dawson
31. Oktober 2013 um 14:43 Uhr
Ich denke nicht, dass es eine gute Idee ist. Die Verwendung des Adapters besteht darin, dass die Elemente nur bei Bedarf im Speicher erstellt werden, und hier initialisieren Sie den Adapter mit der gesamten Liste der Elemente. Dann ist es kein Adapter, oder? Es ist nur ein Manager, der die Daten bei Bedarf weitergibt.
– Viswanath Lekshmanan
24. Dezember 2013 um 5:36 Uhr
Die Lösung, die Sie erwähnen, ist die, die ich anderen Entwicklern in der Vergangenheit empfohlen habe. Geben Sie in getCount() einfach Integer.MAX_VALUE zurück, Sie erhalten ungefähr 2 Milliarden Elemente, was ausreichen sollte.
Danke für deine Antworten. Lösung für große Zahlen 🙂
– Benutzer281076
26. Februar 2010 um 13:54 Uhr
@Romain Guy: Die Verwendung der Hugh Number-Lösung führt zu Leistungsproblemen beim Versuch, eine große Anzahl von Daten zu erhalten.
– Surej
24. April 2012 um 11:57 Uhr
@Surej Ich glaube, Sie versuchen vorzuschlagen, dass die Rückgabe einer “großen Zahl” die Leistung beeinträchtigt. Es wird nicht; eine kurze Quellenangabe zeigt, dass ListViews keine Objekte nur basierend auf dieser Zahl zuweisen. Die einzige Ausnahme, bei der ich mir nicht sicher bin, ist die Verwendung des Layoutmodus LAYOUT_FORCE_BOTTOM da es von unten nach oben befüllt wird, aber ich habe es mir nicht im Detail angesehen.
– Paul Lammertsma
7. April 2013 um 15:29 Uhr
@PaulLammertsma Das Erstellen eines Layouts von unten wirkt sich auch nicht auf die Leistung aus.
– Romain Guy
8. April 2013 um 17:05 Uhr
@RomainGuy seltsamer Fehler- Integer.MAX_VALUE nicht funktionieren (getView nicht aufgerufen) aber Integer.MAX_VALUE - 2 funktionieren wie erwartet. eine Ahnung warum?
– sie
13. Oktober 2014 um 13:27 Uhr
Harry
Ich habe, oder ich denke, ich habe es richtig gemacht, basierend auf den obigen Antworten. Ich hoffe, das wird Ihnen 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 nach oben scrollen und dann nach unten scrollen.
// sehen, wie viele Artikel wir scrollen 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 das “-3”-Zeug nicht machen. Es ist für meine App, richtig zu funktionieren.
Ich konnte einige gute Antworten dafür sehen. Einer meiner Freunde hat versucht, dies über eine einfache Lösung zu erreichen. Überprüf den github Projekt.
ViliusK
Wenn ich LoadersCallbacks verwende, habe ich die Klasse MyCircularCursor erstellt, die den typischen Cursor wie folgt umschließt:
Die Lösung von Herrn Boston ist die einzige verfügbare Option, wenn Sie sich an den Unterricht halten
AdapterView
Hierarchie (z. B.ListView
).– CommonsWare
25. Februar 2010 um 13:01 Uhr
Ich halte mich nicht an irgendeinen Unterricht. Ich erwähne ListView nur, um eine Vorstellung vom Verhalten und Aussehen des Steuerelements zu geben, Sie können es auch “Tabelle” nennen, wenn Sie möchten. Etwas sehr Benutzerdefiniertes könnte eine Reihe von Zellen sein, die eine Liste bilden, und wenn eine dieser Zellen den sichtbaren Bereich verlässt, aktualisiert die Engine (von einem anderen Thread, glaube ich) ihre Position (Koordinaten) und ihren Inhalt (Text + Bilder). . Dieser Aktualisierungsvorgang kann jedoch das reibungslose Scrollen beeinträchtigen.
– Benutzer281076
25. Februar 2010 um 14:49 Uhr
Wo soll ich position=adapter.getcount()/2+x schreiben?
– Schwierig
26. September 2014 um 11:25 Uhr