Java [Android] Wir verwaltet die ListView ihre Elemente?

  • Ersteller Ersteller Tersus
  • Erstellt am Erstellt am
T

Tersus

Gast
Guten Tag,

ich habe zwar viele Tutorials gefunden, wie man eine ListView implementiert, aber nicht, wie diese arbeitet. Nach einigen Tests verwirrt mich die Verwaltung.

Ich habe mir einen eigenen ArrayAdapter inkl. ViewHolder gebastelt.
Der ViewHolder speichert die TextViews gleich mit Text, so dass dieser (Text) sich beim Wiederherstellen bereits intanziierter Objekte nicht ändert.
Als Text habe ich die SpeicherID der jeweiligen Instanz genommen.

Wenn von beispielsweise 50 Elementen, die Zahl ist nicht wirklich entscheidend, 5 auf dem Bildschirm dargestellt werden, verschieben sich diese Elemente beim Scrollen anscheinend willkürlich in der ListView.

Beim Starten der Anwendung:

Die ersten 5 TextViews haben unterschiedliche Speichernummern. Beim Herunterscrollen, erscheinen die 6. und 7. TextView auch noch mit unterschiedlichen Speichernummern. Erst bei der 8. TextView taucht die Speichernummer der ursprünglich 1. TextView auf. Wir verwenden hier also tatsächlich wieder die erste Instanz.
Der weitere Ablauf ist nicht mehr vorhersehbar. Wenn ich wild herauf und herunter scrolle, habe ich irgendwan die Speichernummern an den unterschiedlichsten Stellen und auch völlig neue!
Auch wenn ich ganz an den Anfang scrolle, ist die zum Start der Anwendung erste Speichernummer nicht mehr die selbe. Die Instanzen sind alle wild durcheinander, bzw. manche völlig neu und einige verschwunden.

Würde die ListView jedes Element, welches oben aus der Liste verschwindet, unten anhängen, könnte ein solches Durcheinander doch gar nicht entstehen.


Also bitte kläre mich doch einer auf.
 
Die getItem habe ich tatsächlich nicht überschrieben, aber das ist doch gar nicht nötig. Per getItem wird doch nur die Information (oft ein String) an der entsprechendn Position zurückgegeben und später in einer View verwendet wird (z.B. als Text in einer TextView).
Die getItem hat keinen Einfluss auf die TextView-Instanzen.

Ich habe es auch mit einem BaseAdapter versucht, aber leider ein ähnliche "chaotische" Verwaltung erzielt.

Ich glaube auch nicht, dass es am Adapter liegt. Dieser gibt nur die View-Instanzen an die ListView zurück, welche diese anfordert. Die ListView selbst ist doch für die Verwaltung, also die Entscheidung, welche View sie jetzt vom Adapter reinlädt und welche sie rausschmeist, zuständig.

:freak:
 
zeig mal bitte die Implementierung deiner Methode getView(). Da wird und muss getItem nämlich benutzt werden.
 
Zuletzt bearbeitet:
Redirion schrieb:
zeig mal bitte die Implementierung deiner Methode getView(). Da wird und muss getItem nämlich benutzt werden.

Dieser Beitrag verrät mir, dass wir aneinander vorbei reden.

Noch mal zum Verständnis:
Mein Vorhaben ist, herauszufinden, wo in der späteren ListView meine bereits instanziierten TextViews beim Scrollen auftauchen.

Hier mal mein gesamter Adapter:
Code:
package com.example.test;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class mod_BaseAdapter extends BaseAdapter {
  
  private final Context   _Context;
  private final int     _anzahl = 50;
  
  public mod_BaseAdapter(Context context){
    
    this._Context = context;
    
  }
    
  @Override
  public int getCount() {
    return _anzahl;
  }

  /** Implementiert, jedoch <b>nicht</b> in Verwendung</br>
   * <u>Begründung:</u> Die konkreten Zeichenkettenelemente sind <b>in diesem Fall</b> irrelavent</br>
   */
  @Override
  public Object getItem(int position) {
    
    return "nicht in Verwendung";
    
  }

  @Override
  public long getItemId(int position) {
    return position;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {

    View Ansicht = convertView;

    TextView _TextView;
    
    if(Ansicht == null){
      
      LayoutInflater Inflater = (LayoutInflater) _Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      Ansicht = Inflater.inflate(R.layout.zeilenelement, null);
      
      _TextView = (TextView) Ansicht.findViewById(R.id.Zeilenelement_TextView);
      
      // TextView modifizieren
      {
        _TextView.setTextSize(18 * _Context.getResources().getDisplayMetrics().density);
        _TextView.setText("Zeile: " + position + " | Speicherzelle: " + _TextView.toString().substring(24, 32));
        
      }
      
    
    }
    
    /*  Wir verändern die wiederverwendete TextView nicht!
     *  Daher brauchen wir auch KEINEN ViewHolder.
     *  
     *  Begründung:
     *  Wir wollen herausfinden, wo unsere einst instanziierte
     *  TextView in der Liste später beim Scrollen auftaucht!
     */
    
    return Ansicht;
    
  }

}

Bitte nicht voreilig antworten, von wegen, da fehlt ja alles!

Ich möchte keine dem Adapter übermittelten Daten in den TextViews anzeigen, deswegen übermittlere ich ihm auch gar keine Daten. Ich sage ihm mit der getCount nur, dass wir 50 TextViews darstellen wollen.
Inhalt der TextViews ist deren theoretische Zeile und praktische Speicheradresse.

Deshalb benötigen wir auch keinen ViewHolder, weil wir den Inhalt der wiederverwendeten TextViews gar nicht ändern wollen. Wir wollen, dass der ursprüngliche Inhalt bleibt, um zu sehen, wann beim Scrollen eine bereits instanziierte TextView erscheint.

Wenn wir nun langsam herunter scrollen, sehen wir hier sehr gut, dass eine Wiederverwendung der bereits instanziierten Elemente stattfindet.

Wenn wir jedoch schnell herauf und herab scrollen, geraten die Instanzen völlig durch einander. Wieso?
 
Android versucht Resourcen zu sparen. Eine ListView ist darauf ausgelegt nur die wirklich notwendigen (sichtbaren) Daten im Speicher zu halten.

Die ListView hält intern die Daten nur in WeakReferences. Bei schnellen Scrollen werden Daten schneller als die View selbst invalidiert.

Zwei Ideen zum Probieren für dich (habs selbst heute nicht mehr ausprobiert):
in der xml wird folgende Modifikation die ListView zwingen ihre Daten sofort zu aktualisieren:
android:scrollingCache="false"

Oder "manuell erzwingen" durch folgende Modifikation an deinem Quelltext:

Code:
if(Ansicht == null){
    LayoutInflater Inflater = (LayoutInflater) _Context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    Ansicht = Inflater.inflate(R.layout.zeilenelement, null);
}

also den if-Block auf die rechenintensive Operation mit dem LayoutInflater reduzieren und die restlichen Zeilen immer ausführen lassen.
 
Redirion schrieb:
android:scrollingCache="false"
Das hat nicht geklappt. setScrollingCache hat nichts mit dem laden der Instanzen zu tun, sondern nur mit der grafischen Darstellung.

Redirion schrieb:
also den if-Block auf die rechenintensive Operation mit dem LayoutInflater reduzieren und die restlichen Zeilen immer ausführen lassen.
Ein Erzwingen findet in dem Falle nicht statt. Im Hintergrund werden die instantiierten Views nach wie vor durcheinander geworfen, nur der Text, welcher ihnen zugefügt wird, ist in dem Falle korrekt. Daran sieht man aber auch nicht mehr, um welche Instanz es sich ursprünglich handelte.

In meinem Fall würden die Zeilen nicht durcheinandergeraten, die Speicheradressen aber immernoch.

Ich glaube der OnScrollListener der ListView ist für die Verwaltung zuständig. Sich so tiefgründig mit der Verwaltung zu beschäftigen, ist sicher nicht notwendig. Ich belasse es erstmal hierbei.

Ich verlinke an dieser Stelle mal noch:
http://lucasr.org/2012/04/05/performance-tips-for-androids-listview/


Grüße
 
Tersus schrieb:
In meinem Fall würden die Zeilen nicht durcheinandergeraten, die Speicheradressen aber immernoch.

Beobachte beim Scrollen mal Logcat und achte auf die Einträge vom Garbage Collector. Du kannst ihm bei der Arbeit quasi zusehen.
 
Ich finde die GC Aufrufe in meiner LogCat gar nicht. Wie schauen die aus? Kann ich sie filtern?
 
Die eigentliche Verwaltung der wiederzuverwendenden Views (Scrap Views) obliegt nach meinem Verständnis der (privaten) Klasse RecyleBin im AbsListView.

Verlassen solltest du dich aber auf deine Erkenntnisse sowieso nicht, da die API keinerlei Garantien gibt in welcher Reihenfolge Views recylet werden. In der nächsten API-Version könnte also ganz anders sein, ohne das es angekündigt wird.
 

Ähnliche Themen

Zurück
Oben