ListActivity fertig geladen?

  • Antworten:10
martin schliefellner
  • Forum-Beiträge: 41

01.03.2012, 13:06:46 via Website

Hallo,
kann ich irgendwie festellen wann die Liste von meinem adapter fertig geladen hat?

ich setze in meiner Listactivity den adapter:

1adapter = new ProductlistAdapter(output, this);
2 getListView().setAdapter(adapter);
3 getListView().setOnScrollListener(this);

und dort füge ich die Bildernamen in eine Lokale ArrayListe (data[2] ist der name zb test.jpg):

1ImageViewItem item = new ImageViewItem((ImageView) v.findViewById(R.id.image), data[2]);
2 imageViewItemList.add(item);

Wenn die normalen Daten gesetzt sind möchte ich die Bilder laden:

1ImageDownloader imageDownloader = new ImageDownloader();
2 imageDownloader.execute((ArrayList<ImageViewItem>[]) null);

1private class ImageDownloader extends AsyncTask<ArrayList<ImageViewItem>, Void, Void> {
2
3 @Override
4 protected Void doInBackground(ArrayList<ImageViewItem>... params) {
5 try {
6 Bitmap bitmap = null;
7 for (ImageViewItem tmp : imageViewItemList) {
8 String imageName = tmp.getImageName();
9 String URL_PATH = ("http://www.meineseite.de/images/" + imageName).replace(" ", "%20");
10 File cacheDir = new File(android.os.Environment.getExternalStorageDirectory(), "cache");
11 if (!cacheDir.exists()) {
12 cacheDir.mkdirs();
13 }
14 String mimeType = tmp.getImageName().substring(imageName.length() - 3);
15 File f = new File(cacheDir, String.valueOf(URL_PATH.hashCode()) + "." + mimeType);
16 Log.d("IMAGEDOWNLOADER", "Mimetype is: " + mimeType);
17 if (f.exists()) {
18 bitmap = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
19 tmp.getView().setImageBitmap(bitmap);
20 Log.i("IMAGEDOWNLODER", "Load image from local cache.");
21 }
22 else {
23 URL imageUrl = new URL(URL_PATH);
24
25 HttpURLConnection conn = (HttpURLConnection) imageUrl.openConnection();
26 conn.setConnectTimeout(30000);
27 conn.setReadTimeout(30000);
28 conn.setInstanceFollowRedirects(true);
29 InputStream is = conn.getInputStream();
30
31 OutputStream os = new FileOutputStream(f);
32
33 byte[] buffer = new byte[1024];
34 // copy stream
35 while (true) {
36 int count = is.read(buffer, 0, 1024);
37 if (count == -1) break;
38 os.write(buffer, 0, 1024);
39 }
40 conn.disconnect();
41 is.close();
42 os.close();
43 }
44 bitmap = BitmapFactory.decodeStream(new FileInputStream(f), null, null);
45 tmp.getView().setImageBitmap(bitmap);
46 Log.i("IMAGEDOWNLODER", "Loaded image from weg.");
47 }
48 }
49 catch (Exception e) {
50 e.printStackTrace();
51 return null;
52 }
53 return null;
54 }

Dieser soll mir zu den Imageviews dann die Bilder laden und setzen. Jetzt frage ich mich wie ich den AsyncLoader am besten aufruf bzw. wann - dachte mir wenn er die Liste geladen hat damit er danach die Bilder lädt und setzt?

Hab diese Methode im Web gefunden - jemand ne idee?
lg

Antworten
martin schliefellner
  • Forum-Beiträge: 41

01.03.2012, 13:19:08 via Website

D.h. das mit der ArrayList wo ich die Objekte hinzufüge (Objekt besteht aus String Bildname und ImageView view) passt mal.

Und wann ruf ich den AsyncLoader dann am besten auf? Also im Moment add ich ja alle Bilder mal in die Liste bei der getView Methode und dachte mir erst danach geh ich im AsyncLoader die komplette Liste durch und setz die Bilder - nur frag ich mich wann ich den AsyncLoader am besten aufrufen soll.

Oder rufst du direkt jeden Durchlauf den AsyncLoader auf beim Laden der Liste?

Weil die Liste selber Laden geht ja wie du sagst schneller während der AsyncLoader ja erst das Bilder ausm Web laden muss und anschließend setzt - das dauert länger. Deswegen dachte ich, ich lade zuerst die ganze Liste normal und wenn diese fertig ist lad ich die Bilder nach. Oder soll ich direkt bei jedem Durchlauf den AsyncLoader starten? (nachdem er dazu nen Thread startet wären das wahrscheinlich dann aber jede Menge Threads wenn die Liste zB 80 Einträge lang ist).

lg

lg

Antworten
p a
  • Forum-Beiträge: 131

01.03.2012, 13:30:37 via Website

ja, das wären enorm viele threads, ich würd dir empfehlen den AsyncTask nach dem "Liste laden" zu starten und ihn dann einfach die Bilder einfügen zu lassen, je nachdem ob du Platzhalter verwendest oder nicht verändert sich dann natürlich während des Ladens das Layout der Liste, also Elemente werden größer oder soetwas.

Antworten
martin schliefellner
  • Forum-Beiträge: 41

01.03.2012, 13:34:33 via Website

Hi ok dann noch eine Frage - wie erkenn ich ob die Liste fertig geladen wurde? (Platzhalter verwende ich).

Nach setzen des Adapters in der Listactivity geht er ja direkt weiter - irgendwie muss ich feststellen können "Liste ist fertig" => "Lade bilder nach". Da hab ich nichts passendes gefunden leider.

lg

Antworten
Rafael K.
  • Forum-Beiträge: 2.359

01.03.2012, 13:40:23 via Website

Ich beschreib mal kurz wie ich das gelöst habe.

1. habe ich eine Klasse, die die Bilder lädt und nach URL cached, um Traffic zu sparen.
2. am Ende des onCreate starte ich einen AsyncTask, der die Liste der Elemente übergeben bekommt und EINEN Index für den er das Bild laden soll
3. im onPostExecute dieses AsyncTask wird das Bild in den Listeneintrag geladen und wenn die Activity noch läuft (!isFinishing), passieren 2 Dinge:
4.1 am Adapter wird eine Methode aufgerufen, die ihm sagt, dass sich die Liste geändert hat, damit er sie neu rendert (siehe Doku)
4.2 Der AsyncTask ruft eine neue Instanz von sich selbst auf mit Index+1, wenn die Liste noch nicht komplett durchlaufen wurde

D.h. bei Beenden der Activity wird das Laden der Bilder automatisch beendet, ansonsten lädt er sie halt Stück für Stück.

Nach setzen des Adapters in der Listactivity geht er ja direkt weiter - irgendwie muss ich feststellen können "Liste ist fertig" => "Lade bilder nach". Da hab ich nichts passendes gefunden leider.
Wozu sollte das nötig sein? Sobald der Adapter initialisiert ist, kannst du direkt loslegen.

— geändert am 01.03.2012, 13:41:31

Antworten
martin schliefellner
  • Forum-Beiträge: 41

01.03.2012, 14:08:07 via Website

Ah ich seh grad warum du das mitm Index gelöst hast, anscheinend kann man im doInBackground die Werte nicht setzen da nicht im UI-Thread.
Befindet man sich im onProgressUpdate im UI-Thread?

lg

Antworten
martin schliefellner
  • Forum-Beiträge: 41

01.03.2012, 22:13:37 via Website

Also nach langem herumprobieren und einigen Problemen da es nur halb funktioniert hat hab ich jetzt was anderes versucht.

Diesmal möchte ich eine progressbar anzeigen lassen und die Daten vorher schon währenddessen fertig laden.

Ich starte dazu einen Thread in der Activity und gebe das Objekt von der Activity mit. wenn der Thread fertig ist möchte ich die Liste starten das Problem ist dass ich das nicht machen kann...:
1android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

also beim Runnable-Konstruktor geb ich this mit und möchte dann in der run - Methode am ende Objekt.showList aufrufen was nicht geht. Ich hab gegoogelt und hab gesehen dass es mit einem Handler gehen sollte, jedoch wenn ich mach
Hander h = new Handler();
h.post(myRunnableObject); dann startet er den Thread im der selben Acitivity was wiederum blockiert und das will ich nicht.. wie kann ich das lösen?

lg

Antworten
Florian B.
  • Forum-Beiträge: 284

01.03.2012, 23:23:08 via Website

Das kannst du auch wieder mit einem AsyncTaks machen, die sind meiner Meinung nach leichter zu handhaben.

Du startest zu Beginn deiner Acitvity den AsyncTask der die Bilder herunterlädt. Aus der onProgressUpdate() Methode heraus kannst du die ProgressBar updaten.

Deine Liste erstellst du ganz normal wie vorher auch in der onCreate(), allerdings übergibst du dem Adapter deiner Liste noch keinen Curosr oder Array mit Daten (je nachdem was du verwendest). Du kannst dem Adapter normalerweise einfach null übergeben. Wenn der AsyncTask dann fertig ist, kannst du die ProgressBar verstecken und dann übergibst du erst dem Adapter die Daten in Form eines Cursors oder Array.

Antworten
martin schliefellner
  • Forum-Beiträge: 41

02.03.2012, 01:18:02 via Website

Super geil funktioniert jetzt mit Asyncloader und der Progressbar.

Die Bilder wenn ich sie vom weblade schreibe ich in nen ordner in der Speicherkarte als Cache.
Während der Entwicklung bin ich draufgekommen dass beim Scrollen der Adapter immer komplett neu aufgerufen wird jetzt hab ich versucht das zu cachen aber keine Ahnung ob das etwas bring:

1public View getView(int position, View convertView, ViewGroup parent) {
2 if (!this.cache.containsKey(position)) {
3 CacheView cacheView = this.list.get(position);
4 View v = convertView == null ? getCustomView() : convertView;
5 ((TextView) v.findViewById(R.id.productName)).setText(position + " | "
6 + cacheView.getText());
7 ((TextView) v.findViewById(R.id.productPrice)).setText(cacheView.getPrice());
8
9 if (cacheView.getImage() != null) {
10 ((ImageView) v.findViewById(R.id.image)).setImageBitmap(cacheView.getImage());
11 }
12
13 this.cache.put(position, v);
14
15 return v;
16 }
17 else {
18 return this.cache.get(position);
19 }

Ich frag mich ob das was bringt oder ob View v = convertView == null ? getCustomView() : convertView; das Problem schon löst...
lg

Antworten