ProgressBar & Style

  • Antworten:4
TheEvilOne
  • Forum-Beiträge: 311

26.07.2010, 09:02:37 via Website

Hallo,

ich bin gerade dabei ein bißchen mit ProgressBars rumzuspielen. Dafür habe ich 2 Stück in einer Activity bzw. xml erstellt. Die eine habe ich im Standard-Style erstellt und die andere in einem horizontalen Design mit

1style="?android:attr/progressBarStyleHorizontal"

Mein erstes Problem ist, dass die, die ich im Standard-Design erstellt habe, also ganz einfach:

1<ProgressBar
2android:id="@+id/pb_progressbar_standard"
3android:layout_width="wrap_content"
4android:layout_height="wrap_content"

falsch angezeigt wird. Hier habe ich mal ein Screenshot. Eigentlich sollte die obere ProgressBar aber doch so aussehen wie hier.

Kann mir jemand sagen, was ich da falsch gemacht habe?




Und zu meinem zweiten Problem:

In der Activity starte ich einen Thread, in dem ich einen int-Wert hochzähle und das an die horizontale ProgressBar weitergebe. Diese zählt somit hoch.

Hier erstmal der Code der Activity:

1package de.dev;
2
3import android.app.Activity;
4import android.content.Intent;
5import android.os.Bundle;
6import android.os.Handler;
7import android.os.Message;
8import android.view.View;
9import android.widget.Button;
10import android.widget.ProgressBar;
11import android.widget.TextView;
12
13public class ProgressBarActivity extends Activity{
14
15 private ProgressBar pb_horizontalProgressBar;
16 private int myProgress = 0;
17 private TextView tvStatus;
18 private Handler myHandle = new Handler();
19
20 @Override
21 public void onCreate(Bundle savedInstanceState) {
22 super.onCreate(savedInstanceState);
23 setContentView(R.layout.layout_progressbar);
24
25
26 pb_horizontalProgressBar = (ProgressBar)findViewById(R.id.pb_progressbar_horizontal);
27 tvStatus = (TextView)findViewById(R.id.lb_status);
28
29 new Thread(myThread).start();
30
31 Button btZurueck = (Button)findViewById(R.id.bt_zurueck);
32 btZurueck.setOnClickListener(new View.OnClickListener() {
33 public void onClick(View view) {
34
35 Intent intent = new Intent();
36 setResult(RESULT_OK, intent);
37 finish();
38
39 }
40 });
41 }
42
43
44 private Runnable myThread = new Runnable(){
45
46 @Override
47 public void run() {
48
49 tvStatus.setText("Status: In Progress...");
50
51 while (myProgress < 100){
52 try{
53 myHandle.sendMessage(myHandle.obtainMessage());
54 Thread.sleep(50);
55 System.out.println(myProgress);
56
57
58 myHandle.post(new Runnable() {
59 public void run() {
60 myProgress++;
61 pb_horizontalProgressBar.setProgress(myProgress);
62 }
63 });
64
65 } catch(Throwable t){
66 t.printStackTrace();
67 }
68
69 }
70 }
71 };
72}

Ich übergebe dem TextView im Thread den String "Status: In Progress...". Wenn der Thread fertig bzw. beendet ist, möchte ich dass der Status der TextView sich in "done" ändert. Und eben genau hier ist der Knackpunkt. Ich weiß nicht, wie ich herausfinden kann, wann der Thread beendet ist. Wenn ich nach der while-Schleife versuche dem TextView über die setText() einen String mitzugeben, bekomme ich hier eine Exception, dass dies im falschen Thread übergeben worden ist. Über eine Callback-Methode habe ich es auch schon versucht, das hat aber irgendwie auch nicht funktioniert.

Kann mir hier jemand einen Tipp geben?

Danke und viele Grüße

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

26.07.2010, 13:58:15 via Website

Man wartet auf das ende eines Thread z.b über Thread#join() oder man benutzt eine condition variable welche du aber volatile deklarieren solltest damit das auch klappt:

1private volatile boolean isThreadFinished = false;

in deinem Thread setzt du entsprechend den Wert.

Man kann ebenfalls mittels Object#notify/notifyAll und Object#wait das ganze lösen, allerdings ist das eher Thema eines "Thread" Buches.

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
TheEvilOne
  • Forum-Beiträge: 311

26.07.2010, 15:13:51 via Website

Danke für Deine Antwort. Aber an welcher Stelle muß ich denn dann die isThreadFinished-Variable abfragen (oder den Thread#join() realisieren)?

In der onCreate-Methode kann das ja nicht geschehen, denn wenn ich dort join oder wait aufrufe, wird die Activity nicht komplett aufgebaut bzw. abgearbeitet. Dann müßte ich doch eigentlich noch irgendeinen Listener o.ä. einbauen, über den ich die Benachrichtigung bekomme, dass der Thread beendet ist, oder?


Mh, vielleicht gehe ich die Sache auch verkehrt herum an. Ich möchte ja eigentlich nur, dass wenn die ProgressBar abgearbeitet wurde, sich der Text der TextView ändert. Wenn ich aber nach der while-Schleife (nach der Zeile 62) folgenden Code einfüge:

1if(myProgress >= 100){
2 tvStatus.setText("Status: Done!");
3}

bekomme ich folgende Exception:

1android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread craeted a view hierarchy can touch its views.

Kannst Du mir hier einen Tipp geben?

Danke und viele Grüße

Antworten
Mac Systems
  • Forum-Beiträge: 1.727

26.07.2010, 16:16:41 via Website

Viele Wege führen nach Rom. Du kannst ebenfalls ein Interface definieren:

1public interface Callback
2{
3 public void methodFinished();
4}


1public MyActivity extends Activity implements Callback
2{
3
4 public void methodFinished()
5 {
6 // mach was
7 }
8}


Und dieses deine Aktivität implementieren lassen und bei ende des Threads einfach das Interface aufrufen. Die Methode #methodFinished wird aber dennoch noch aus dem Thread aufgerufen daher auch hier wieder ein wenig Vorsicht mit der Nebenläufigkeit!

Das Thread#join in der onCreate nichts zu suchen hat sollte klar sein, da du den Main Thread blockierst was man nicht tun soll! Da ist ähnlich wie in Java/Swing mit dem EDT! Hier würde nur ein weiter Thread sinn machen.

In etwa so könnte man das machen, da hier aber schon zwei Threads benutzt werden und ein Mobile eher limitierte Ressourcen hat ist das Callback Interface eher die bessere Wahl.

1public void onCreate(...)
2{
3 // init code .. worker Thread
4
5 final Thread waitingThread = new Thread(new Runnable()
6 {
7 public void run()
8 {
9 workerThread.join();
10 doSomethingAfterThreadEnd();
11 }
12 }
13 waiter.start();
14}



Wieso deklarierst du variablen die von verschiedenen Threads genutzt werden nicht volatile ?
1private volatile int myProgress = 0;

hth,
Mac

— geändert am 27.07.2010, 01:15:43

Windmate HD, See you @ IO 14 , Worked on Wundercar, Glass V3, LG G Watch, Moto 360, Android TV

Antworten
TheEvilOne
  • Forum-Beiträge: 311

27.07.2010, 08:00:30 via Website

Ich hatte das mit dem Interface auch schon mal probiert, nur leider bekam ich dort die selbe Exception.

Ich habe das Beispiel mal in ein extra Projekt gepackt und hochgeladen. Könntest Du Dir, wenn Du mal Zeit hast, das bitte mal kurz anschauen?

Hier kannst Du das Projekt (Eclipse Android-Projekt, Android Version 2.1) runterladen.

Die Klasse de.dev.IThreadCallBack beschreibt das Interface und die Klassen, um die es "hier geht" ist die de.dev.ProgressBarActivity. Dort wird in der Zeile 46 der Thread gestartet und in Zeile 78 sollte das Interface aufgerufen werden.

Was mich an der ganzen Sache wundert ist, dass ich das TextView schonmal in der Zeile 55 (innerhalb des Threads) anspreche und es dort auch funktioniert. Spreche ich das TextView allerdings nochmal nach der Schleife in Zeile 78 an, bekomme ich eine Exception.

Schonmal Danke und viele Grüße

Antworten