![]() |
Listview mit Firemonkey
Hi,
hat jemand eine Idee, wie ich mit TListview ~2500 Einträge schnell befüllen kann und die Bitmaps in dem jeweiligen TListviewItem nur zeichne sobald das nötig ist? So wie ich das gesehen habe gibt es keine Cachefunktion, so dass im schlimmsten Fall für die 2500 Einträge 128x128 Pixel pro Bitmap der Speicher des Mobilgerätes schnell voll ist. Peter |
AW: Listview mit Firemonkey
2500 Eintrage? Da scrollt man sich ja zu Tode, wenn man was sucht.
|
AW: Listview mit Firemonkey
Also normalerweise erstellt FMX die Objekte erst dann, wenn sie angezeigt werden sollen und verwirft sie wieder, wenn sie außer Sicht sind. So ist das zumindest beim Grid, aber ich gehe davon aus, dass es bei der Listview entsprechend ist. Teste es doch einfach einmal aus.
|
AW: Listview mit Firemonkey
Zitat:
|
AW: Listview mit Firemonkey
Normalerweise ist das unsinnig. Es handelt sich Webcams, und die Datenbank die ich hier habe beinhaltet den Stream, einen Link zum aktuellen Screenshot, den Namen der CAM und eine kurze Erklärung. Leider ist das ganze nicht in Regionen aufgeteilt, so dass ich eventuell ein virtuelles Directory erstellen kann (quasi nach Region->Land->Stadt->Cams).
Für den Test habe ich erst einmal nur eine Liste gefüllt:
Delphi-Quellcode:
procedure TForm2.Button1Click(Sender: TObject);
var i: integer; item: TListViewItem; begin ListView1.BeginUpdate; for i := 0 to 4000 do begin item := ListView1.Items.Add; item.Text := format('channel %d', [i]); item.Detail := 'lorem ipsum' + sLineBreak + 'lorem ipsum'; item.Bitmap.SetSize(128,128); item.Bitmap.Clear($FFFF0000); end; ListView1.EndUpdate; end; Das dauert auf dem PC schon 3 Sekunden, auf einem Mobilgerät will ich das gar nicht erst probieren. Peter |
AW: Listview mit Firemonkey
Zitat:
|
AW: Listview mit Firemonkey
Dann bliebe evtl.noch die Option, die Bitmap nicht für die einzelnen Listview-Items zu erzeugen, sondern nur im Onpaint-Event zu zeichnen. Wenn ich das richtig in Erinnerung habe, gibt es aber kein Paint-Event für die einzelnen List-Items, man müsste also selber berechnen, welche Listview-Items gerade sichtbar wären und wo was beim OnPaint-Event der Listview zu zeichnen wäre.
Leider muss ich im Moment mit einer konkreten Lösung dafür passen... |
AW: Listview mit Firemonkey
Oder: man erzeugt z.B., erst mal die ersten 20 Bitmaps für die ersten ListViewItems. Dann reagiert man auf das ändern des aktuellen Indexes und erzeugt eine entsprechende Anzahl von Bitmaps für die ListViewItems davor und dahinter (soweit noch nicht vorhanden).
Das ist eine gängige Technik, wie man das z.B. beim Rendern von PDF-Seiten anwendet. |
AW: Listview mit Firemonkey
Oder du guckst mal hier:
![]() Bei so vielen Einträgen ist das ganze aber auch am Limit. Vor allem das Befüllen dauert ewig, da jedes Item ein Bitmap für den Cache hat und einige Unterklassen initialisiert. Bei ein paar Dutzend Einträgen merkt man das nicht, bei ein paar hundert aber schon. Christian |
AW: Listview mit Firemonkey
Ich würde versuchen das in Kategorien zu pressen, so daß eine wie auch immer konstruierte Master Detail Beziehung aufgebaut werden kann, und einzelne Listviews eben nicht mehr als 50 Einträge beinhalten. Gerade Webcams lassen sich ja eigentlich geographisch gliedern.
Sherlock |
AW: Listview mit Firemonkey
Wie wäre es, wenn du extra ein TImage/TPaintbox oberhalb der Liste hättest und nur dort das Vorschaubild des aktuell selektierten/fokussierten Items anzeigst?
|
AW: Listview mit Firemonkey
Zitat:
Ein Grid/Liste mit Filterfunktion wäre da fast schon angebracht. |
AW: Listview mit Firemonkey
Zitat:
|
AW: Listview mit Firemonkey
Grundsätzlich stellt das kein Problem dar wenn man sich so bekannte Shopping-Apps von amazon oder ebay anschaut. Bislang habe ich dort keine Probleme gehabt auch mit riesigen Trefferlisten.
(Nein, gerade eine Trefferliste mit 1250 Einträge angeschaut - kein Problem und langsam ist es auch nicht) (Und noch eine mit >13000 Einträgen machte auch keine Probleme) |
AW: Listview mit Firemonkey
Zitat:
Die Frage war ja nicht, ob es sinnvoll ist 2500 zu verwenden, sondern eine andere. Wir kennen den Kontext nicht. Und ja, manchmal ist es einfacher in einer großen Liste mit scrollen etwas zu suchen, als vorher einen Filter einzugeben. |
AW: Listview mit Firemonkey
Da will ich auch mal...
Ich finde auch, dass ein Control/Framework generell performant mit großen Listen klar kommen muss. Ob man dann (zusätzlich) Filterung, Gruppierung und Umsortierung anbietet steht auf einem anderen Blatt. WENN man (z.B. auch nur in einem Wartungsbereich) eine reale komplette Liste einsehen will, sollte das auf jeden Fall schnell und problemlos möglich sein. |
AW: Listview mit Firemonkey
Liste der Anhänge anzeigen (Anzahl: 1)
Oh sinnvoll ist sowas schon. Bei meiner Sat>IP Anwendung habe ich im ungünstigsten Fall 3000 Einträge wenn ich Astra 19.2, 23. 28 und Hotbird 13 berücksichtige. Das wäre schon nett, wenn das performant gelistet werden könnte, zumal die Dekodierung von Mpeg2 und H264 mitunter arg an Leistung benötigt. Da ist das schon ein bisschen peinlich, wenn da die simple Liste mehr Leistung verbrät als die Videodarstellung :?
|
AW: Listview mit Firemonkey
Ich habe gestern vergessen zu erwähnen, dass du bei der Treeview-Komponente von Kernowsoftware auf Mobilen Geräten im OnScrollviewChange Ereignis ClearCache(TksClearCacheType.ksClearCacheNonVisibl e); aufrufen solltest.
Ansonsten cached er dir (in der aktuellen Version zumindest) jeden Eintrag und eh du dich versiehst ist der Speicher deines Telefons bzw. Tablets voll. Die Geschwindigkeit ist jetzt auch nicht das Nonplusultra, aber es ist erträglich. Eine reine virtuelle Treeview-Komponente wäre sicherlich besser. Mal gucken ob ich damit mal beginne :) |
AW: Listview mit Firemonkey
Liste der Anhänge anzeigen (Anzahl: 2)
Ich habe meine beiden weiter oben gemachten Vorschläge mal umgesetzt und hab somit Deinen Beispielcode etwas geändert und im Painting-Event Source eingefügt, der die Bitmaps quasi "on the fly" erzeugt.
Maximal leben immer nur 60 Bitmaps zur gleichen Zeit, abhängig von der Variable Range, die Du halt bei Bedarf ändern kannst. Der Speicher wird also kaum belastet. So wird die Liste quasi ohne Verzögerung erstellt und auch das Zeichnen erfolgt ohne Verzögerung (jedenfalls auf dem PC, wie es auf dem mobilen Teil ist, musst Du selber ausprobieren, hab hier gerade nichts im Zugriff zum Testen --> Hier wäre eine kurze Rückmeldung von Interesse).
Delphi-Quellcode:
Anliegend ein Screenshot und das Demo-Projekt (mit Delphi Seattle erstellt).
procedure TForm19.Button1Click(Sender: TObject);
var i: integer; item: TListViewItem; begin ListView1.BeginUpdate; for i := 0 to 4000 do begin item := ListView1.Items.Add; item.Text := format('channel %d', [i]); item.Detail := 'lorem ipsum' + sLineBreak + 'lorem ipsum'; // item.Bitmap.SetSize(128,128); // item.Bitmap.Clear (TAlphaColorRec.red); end; ListView1.EndUpdate; end; procedure TForm19.ListView1Painting(Sender: TObject; Canvas: TCanvas; const ARect: TRectF); var lvi: TListViewItem; L, start, stop, range, TopItem: Integer; begin if Listview1.ItemCount = 0 then exit; range := 20; TopItem := Round (Listview1.ScrollViewPos / Listview1.ItemAppearance.ItemHeight); Start := TopItem; Stop := Start + range; for L := Start to Stop-1 do begin if L <= Listview1.ItemCount-1 then begin lvi := Listview1.Items[L]; if lvi.Bitmap.Width = 0 then begin lvi.Bitmap.SetSize(128,128); lvi.Bitmap.Clear (TAlphaColorRec.red); end; end; end; // Bei Bedarf if Start > 50 then begin for L := Start-20 downto 0 do begin lvi := Listview1.Items[L]; if lvi.Bitmap.Width <> 0 then begin lvi.Bitmap.SetSize(0,0); end; end; end; if Stop + 20 <= Listview1.ItemCount-1 then begin for L := Stop + 20 to Listview1.ItemCount-1 do begin lvi := Listview1.Items[L]; if lvi.Bitmap.Width <> 0 then begin lvi.Bitmap.SetSize(0,0); end; end; end; end; Edit: Habe doch noch ein mobiles Teil gefunden (Samsung GT-N5100) und schnell mal selbst geteset. Das funktioniert absolut flüssig, null Verzögerung, dabei ist das Gerät noch nicht mal das schnellste. Man sollte aber für Range mehr als 20 Zeilen wählen, da auf größeren Geräten diese Anzahl schnell erreicht ist. 40-60 wären da wohl besser. Edit2: Wenn ich es mir genau überlege, könnte man die benötigte Range (also Anzahl der ListItems, die in der ListView maximal zur gleichen Zeit sichtbar sind) auch anhand der Höhe der Listview berechnen. |
AW: Listview mit Firemonkey
Hey, ich hab ja ein echtes Projekt hier - da kann ich das ganze gleich mal mit dem ksTableView gegentesten. Ich poste dann mal die Performance.
Erstaunlicherweise ist das sogar ein bisschen schneller als die Variante mit ksTableView und es lädt nicht so viele Bilder im Hintergrund. Eventuell musst man noch testen ob TopItem < 0 ist bzw. Stop >= Items.Count. @Harry: Das ist eventuell etwas ab vom Thema, aber hast du ne Idee wie ich mit ListView einen Eintrag via Drag&Drop verschieben kann? Die Ereignisse haben ja kein Target-Objekt, sondern nur die Position. Christian |
AW: Listview mit Firemonkey
Zitat:
![]() |
AW: Listview mit Firemonkey
Hmm, danke - ich dachte das geht etwas leichter aber es sind ja FMXObjekte, deswegen wohl eher nicht.
Wie verschiebe ich eigentlich in der Liste einen Eintrag, ohne die Liste neu zu erstellen? Christian |
AW: Listview mit Firemonkey
Also, mir ist da kein spezieller Befehl zum Verschieben bekannt (Listview.exchange funktioniert nicht mit ListviewItems).
Du musst es also selber erledigen:
Delphi-Quellcode:
procedure TForm19.ListView1DragDrop(Sender: TObject; const Data: TDragObject;
const Point: TPointF); var obj: IControl; idx, add: Integer; lvi: TListviewItem; f: TFMXObject; begin obj := ObjectAtPoint (Screen.MousePos); if obj <> NIL then begin Idx := GetIndexATPos (ListView1, Point); if Idx <> -1 then begin // verschieben vor oder nach aktueller Selektion if TListViewItem (Data.Source).Index > Idx then begin add := 1; end else begin add := 0; inc (idx); end; ListView1.BeginUpdate; lvi := Listview1.Items.AddItem(idx); lvi.Text := TListViewItem (Data.Source).Text; lvi.Detail := TListViewItem (Data.Source).Detail; lvi.bitmap.Assign(TListViewItem (Data.Source).bitmap); Listview1.Items.Delete (TListViewItem (Data.Source).Index+add); ListView1.EndUpdate; Listview1.Selected := lvi; end; end; end; |
AW: Listview mit Firemonkey
Ah Danke, ja so hab ich das auch in der Art gemacht. Es widerstrebt mir aber das als "saubere" Lösung zu akzeptieren :)
Kann ich eigentlich das Bild für den Drag-Effekt weniger verwaschen darstellen?
Delphi-Quellcode:
Christian
procedure TfrmSettingsSort.lvStationsMouseMove(Sender: TObject;
Shift: TShiftState; X, Y: Single); var r: TRectF; bmp: TBitmap; begin if (ssLeft in Shift) and (lvStations.Selected <> NIL) then begin r := lvStations.GetItemRect(lvStations.ItemIndex); bmp := TBitmap.Create(round(lvStations.Width), round(lvStations.Height)); try bmp.Canvas.BeginScene; lvStations.PaintTo(bmp.Canvas, RectF(0, 0, bmp.Width, bmp.Height)); bmp.Canvas.EndScene; FDragBitmap.SetSize(round(r.Width), round(r.Height)); FDragBitmap.Canvas.BeginScene; FDragBitmap.Canvas.DrawBitmap(bmp, r, RectF(0, 0, FDragBitmap.Width, FDragBitmap.Height), 1); FDragBitmap.Canvas.EndScene; finally bmp.Free; end; BeginInternalDrag(lvStations.Selected, FDragBitmap); end; end; |
AW: Listview mit Firemonkey
Falls sich jemand wundert, warum Drag&Drop nicht auf Mobilgeräten geht. Hier ist ein Workaround:
![]() Im Prinzip muss man eine eigene Klasse für IFMXDragDropService hinzufügen. |
AW: Listview mit Firemonkey
Wenn man wirklich 2500+ Bitmaps laden will dann sollte man die vielleicht nicht alle im Speicher halten.
Mit dem Ansatz von Harry könnte man ein Fenster in den Daten laden, und die Bitmaps entsprechend nachladen. Weil ich mit diversen LowMemory Applikation Events zu kämpfen hatte (habe) frage ich mich ständig: Wie speichere ich am Besten meine Bilder (Thumbnails) auf den Mobile Devices ? - Nachladen von File ist nicht so besonders performat - Gespeichert in Sqlite Datenbank, aber legt Sqlite das doch wieder voll im Speicher an ? - Irgendeine andere Datenbank-Lösung, statt Sqlite ? FdMemoryTable liegt wohl auch komplett im Speicher, oder nicht ? - Benutzen eines OnCalculateFields Events, um die Bitmaps dynamisch zu generieren ? - Speichern in einer eigens dafür erstellten Klasse ? Die perfekte Antwort dafür suche ich noch, es scheint alles Vor- und Nachteile zu haben. Rollo |
AW: Listview mit Firemonkey
Kleine Zwischenfrage zu Deinen Gedanken, Rollo: Warum glaubst Du, daß Bilder aus Dateien zu laden weniger Performant sein soll als sie aus einer SQLite DB zu ziehen? Du weisst, daß SQLite dateibasiert ist? Nur mal so als Denkanstoß ;)
Sherlock |
AW: Listview mit Firemonkey
Hallo Sherlok,
ja, richtig. Ich glaube aber einfach das Datenbanken solche Prozesse super-optimiert haben (Wunschdenken). Es geht ja um kleinere Thumbnails, größere Bilder würde ich auch immer über Linik nachladen. Aber wenn man auf MobileDevices eine ListView mit kleinen Vorschaubildern machen möchte wäre doch eine einfache Datenbank dafür elegant. Jedenfalls ist mir noch nicht klar was hier generell der Beste Weg ist. Rollo |
AW: Listview mit Firemonkey
ich steh gerade ein bisschen auf dem Schlauch, aber gibt es eine Möglichkeit diverse TListViewItems zu verstecken? Sowas wie eine Visible Eigenschaft, als listview.items[1].visible := false ist ja nicht implementiert.
|
AW: Listview mit Firemonkey
Zitat:
Sherlock |
AW: Listview mit Firemonkey
Zitat:
Steht auch irgendwo in der Doku von SQLite, dass ab einer bestimmten Datengröße die Performance unterirdisch ist. ![]() |
AW: Listview mit Firemonkey
Zitat:
Delphi-Quellcode:
Hat jemand mal zum Spaß 5000 Einträge oder mehr in eine TListView geschrieben? Das geht recht schnell, aber nach dem Endupdate wird jede Einzelne TListViewItem initialisiert und TAppearanceListView.ResetViewAppearance aufgerufen. Dort erfolgt ein LItemObjects.ResetObjects(AItem, GetFinalItemSize); und zwar von allen Einträgen. Das ganze wäre wesentlich performanter, wenn das nur die sichtbaren Objekte beträfe. So dauert es 3 Sekunden auf einem Android und da kommt dann gerne ein Anwendung reagiert nicht mehr vom System.
for LItem in FNewItems do
LItem.CreateObjects; |
AW: Listview mit Firemonkey
Hab gerade kein Delphi hier, aber haben ListviewItems einen Parent? Wenn ja kann man ja dadrüber die Sichtbarkeit steuern. Alternativ Height := 0.
|
AW: Listview mit Firemonkey
Zitat:
Delphi-Quellcode:
und
http://docwiki.embarcadero.com/Libraries/XE7/en/FMX.ListView.TCustomListView.OnPullRefresh
![]() Aber ich würde mir natürlich wünschen das so etwas clever in die LiveBindings & Datenbankabfragen integriert ist, das fensterweise nachladen. Ich vermute mal man muss ales von Hand machen, siehe auch ![]() Jedenfalls lade ich meine Thumbnail-Bilder im Moment als absoluter FileName aus der Sqlite-Tabele, und lade die dann damit nach in ein MultiResourceBitmap. In der Hoffnung das niemand tausende Einträge damit macht, aber meine Wunschvorstellung davon wäre eine andere. Z.B. nur LiveBindings Verbindung von DataSet zu ListView, welche dann automatisch PullToRefresh macht. Was ich noch testen wollte wäre die Bitmaps als Calculated Field in der DB AdHoc zu Erzeugen, aber das bringt mir im Moment keine Vorteile. Vielleicht hat das ja auch schon jemand versucht, das hätte den Vorteil das man sich nicht gezielt um die Position kümmern muss. Aber die Frage ist noch wo die Instanz vom Bmp dann eigentlich gespeichert werden soll, ImageList war nicht unproblematisch, im ListView selbst war auch nicht optimal, ich mache im Moment ein MultiResBitmap und verlinke im ListViewItem nur die BitmapRef. Rollo |
Alle Zeitangaben in WEZ +1. Es ist jetzt 03:51 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz