ProgressDialog, wyświetlanie paska postępu w oknie dialogowym | android4devs


Lipiec 28, 2011
Okna dialogowe Tutoriale UI

Szczególnym przypadkiem okna dialogowego w Androidzie jest ProgressDialog. Obiektem tej klasy jest bowiem okienko informujące o postępie wykonywanej czynności (lub po prostu o tym, że coś się dzieje gdzieś „w tle”). Jego zastosowanie w interfejsie każdej aplikacji jest niezastąpione bowiem kto z nas lubi zastanawiać się czy nasza aplikacja jeszcze coś robi, czy już się „zwiesiła”?

Dzisiejszy artykuł będzie demonstracją wykorzystania okna dialogowego wyświetlającego pasek postępu pracy nowego wątku naszej aplikacji.

W skład naszej aplikacji wejdą  3 ważne obiekty (klasy dwóch ostatnich stworzone będą przez nas):

I teraz pytanie – w jakim celu wykorzystywany jest ProgressHandler? Czy nie lepiej byłoby modyfikować ProgressDialog bezpośrednio z poziomu wątku ProgressThread? Nie.
Jak się bowiem okazuje, wszelkie obiekty interfejsu użytkownika działają w tzw. UI thread, czyli głównym wątku naszej aplikacji. Nie jest on thread-safe, w związku z czym nie powinniśmy przeprowadzać w nim żadnych asynchronicznych operacji. I w związku z tym nie jest możliwe modyfikowanie elementów UI z poziomu wątku innego niż wątek główny.
Aby umożliwić takie modyfikacje (np. aktualizacje postępu w naszym pasku) powinniśmy wykorzystywać obiekt Handler, który nasłuchuje powiadomień przychodzących z osobnego wątku i na ich podstawie modyfikuje elementy interfejsu (bo działa w wątku głównym).

Aby uniknąć nadmiaru tłumaczenia zakładam, że przed przystąpieniem do tego artykułu czytelnik zapoznał się z tematem wprowadzającym do okien dialogowych – link.

Nasza aplikacja będzie składała się z 1 przycisku, który po kliknięciu uruchomi wątek wykonujący jakąś czynność w tle. Informacje o postępie tej czynności mają być wyświetlane w naszym oknie dialogowym.

Zaczynamy od:

Implementacją zaznaczonej metody zajmiemy się za chwilę. Najpierw dodamy pola naszych trzech niezbędnych obiektów, o którym wspominałem powyżej:

W tym momencie interesuje nas przede wszystkim obiekt naszego Handlera. Stworzyliśmy tu bowiem klasę anonimową, która obsługuje w zdefiniowany przez nas sposób wiadomości przychodzące z osobnego wątku.

Nim omówimy naszą implementację, zatrzymamy się na chwilę by przyjrzeć się możliwościom jakie daje nam klasa Message. Posiada ona 3 istotne pola:

W wielu przypadka bowiem wiadomości przesyłane do Handlera zawierają niewielką ilość informacji. W takich wypadkach powinniśmy wykorzystać powyższe pola do ich przenoszenia (oczywiście jeżeli tylko jest to możliwe).

W przypadku, gdy potrzebujemy przesłać większą ilość informacji, wystarczy do naszej wiadomości dołączyć obiekt Bundle z wszystkimi danymi.

Po więcej danych na temat klasy Message odsyłam do dokumentacji.

Wracając do naszej implementacji… Obiekt Message, który odbiera nasz Handler będzie zawierał dwie informacje. Postęp (liczba całkowita 0-100) oraz stan w jakim aktualnie jest nasz wątek. Postęp będzie wstawiany bezpośrednio do okna dialogowego (metoda setProgress(…) – linijka 23), natomiast stan będzie służył do sprawdzenia, czy przypadkiem wątek nie skończył pracować. Jeżeli tak się stanie, nasze okno dialogowe najpierw zostanie ukryte (metoda dismissDialog(…) ), a następnie usunięte z pamięci (metoda removeDialog(…) ). O drugiej czynności musimy pamiętać szczególnie wtedy gdy kolejne uruchomienie okna dialogowego będzie wymagało nowych danych. W naszym przypadku, gdybyśmy nie usunęli go z pamięci, okno wyświetliłoby się ze stanem z poprzedniego wyświetlenia (czyli z paskiem ustawionym na 100%).

Teraz pozostało nam tylko zaimplementowanie metody uruchamiającej nasz wątek oraz wyświetlającej okno dialogowe:

Idą kolejno:

Powyżej zaprezentowany został kompletny kod MainActivity.java.

Pozostała nam jeszcze implementacja naszego wątku. Nie ma tam jednak tak naprawdę nic ciekawego, w związku z czym zrobimy szybki przegląd.

Wątek po uruchomieniu wykonuje w pętli czynność, która jest nieco opóźniana (Thread.sleep()). W każdym przebiegu inkrementuje on wartość postępu, opcjonalnie zmienia swój stan i wysyła te dane do naszego wątku głównego. W praktyce wygląda to tak:

Cały projekt dostępny jest na naszym Githubie – HelloProgressDialog.

, ,

Zaloguj się lub skorzystaj z profilu:

Możesz również zostawić komentarz bez rejestracji, korzystając z poniższego formularza:

Musisz być zalogowany aby móc pisać komentarze.

ProgressDialog, wyświetlanie paska postępu w oknie dialogowym

Powiązane linki

O szczegółach technicznych

Przykładowa aplikacja

Przygotowania

Kod źródłowy głównej Aktywności

ProgressThread

Screeny

Kompletny kod źródłowy

Komentarze (0)

Prześlij komentarz

Klasa Message

  • ProgressDialog – okno dialogowe z paskiem postępu,
  • ProgressThread – wątek wykonujący jakąś czynność w tle,
  • ProgressHandler – obiekt, który służy do komunikacji pomiędzy dwoma powyższymi.
  • int arg1,
  • int arg2,
  • Object obj
  • linijka 49 – wyświetlenie okna dialogowego, które konstruowane jest w dwóch metodach – onCreateDialog(…) oraz createProgressDialog(). Ten krok został w większości omówiony w artykule związanym z oknami AlertDialog.
    Uwagę należy zwrócić jedynie na linijkę 67, gdzie wskazujemy na jeden z dwóch styli naszego okna dialogowego. Pierwszy z nich to ProgressDialog.STYLE_HORIZONTAL wyświetlający pasek postępu. Drugi natomiast to ProgressDialog.STYLE_SPINNER wyświetlający kręcący się okrąg.
  • linijka 50,51 – utworzenie obiektu wątku (wraz z przekazaniem mu naszego obiektu progressHandler) i wystartowanie go.
public class MainActivity extends Activity {   
	private static final int PROGRESS_DIALOG = 1;

	private Button btnStart;

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initButton();
	}

	private void initButton() {
		btnStart = (Button) findViewById(R.id.btnStart);
        btnStart.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				startWorkerThread();
			}
		});
	}
}
public class MainActivity extends Activity {   
	private static final int PROGRESS_DIALOG = 1;

	private Button btnStart;
    private ProgressDialog progressDialog;
    private ProgressThread progressThread;
    final private Handler progressHandler = new Handler() {
    	public void handleMessage(Message msg) {
    		int progress = msg.arg1;
    		int state = msg.arg2;
    		progressDialog.setProgress(progress);
    		if(ProgressThread.STATE_COMPLETE == state) {
    			dismissDialog(PROGRESS_DIALOG);
    			removeDialog(PROGRESS_DIALOG);
    		}
    	};
    };

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initButton();
	}

	private void initButton() {
		btnStart = (Button) findViewById(R.id.btnStart);
        btnStart.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				startWorkerThread();
			}
		});
	}
}
package pl.froger.helloprogressdialog;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity {   
	private static final int PROGRESS_DIALOG = 1;

	private Button btnStart;
    private ProgressDialog progressDialog;
    private ProgressThread progressThread;
    final private Handler progressHandler = new Handler() {
    	public void handleMessage(Message msg) {
    		int progress = msg.arg1;
    		int state = msg.arg2;
    		progressDialog.setProgress(progress);
    		if(ProgressThread.STATE_COMPLETE == state) {
    			dismissDialog(PROGRESS_DIALOG);
    			removeDialog(PROGRESS_DIALOG);
    		}
    	};
    };

	@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initButton();
	}

	private void initButton() {
		btnStart = (Button) findViewById(R.id.btnStart);
        btnStart.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				startWorkerThread();
			}
		});
	}

	private void startWorkerThread() {
		showDialog(PROGRESS_DIALOG);
		initProgressThread();
		progressThread.start();
	}

	private void initProgressThread() {
		progressThread = new ProgressThread(progressHandler);
	}

	@Override
	protected Dialog onCreateDialog(int id) {
		if(PROGRESS_DIALOG == id)
			return createProgressDialog();
		return null;
	}

	private Dialog createProgressDialog() {
		progressDialog = new ProgressDialog(this);
		progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
		progressDialog.setMessage("Loading...");
		progressDialog.setProgress(0);
		progressDialog.setCancelable(false);
		return progressDialog;
	}
}
package pl.froger.helloprogressdialog;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

public class ProgressThread extends Thread{
	public static final int STATE_BEGIN = 0;
	public static final int STATE_MIDDLE = 1;
	public static final int STATE_END = 2;
	public static final int STATE_COMPLETE = 3;
	
	private Handler handler;
	private int state;
	int progress;
	
	public ProgressThread(Handler handler) {
		this.handler = handler;
	}
	
	@Override
	public void run() {
		state = STATE_BEGIN;
		progress = 0;
		while (state != STATE_COMPLETE) {
			pause();
			increaseProgress();
			sendProgressToHandler();
		}
	}
	
	private void pause() {
		try {
			Thread.sleep(50);
		} catch (Exception e) {
			Log.e("HelloProgress", e.getLocalizedMessage());
		}
	}
	
	private void increaseProgress() {
		switch (++progress) {
		case 30:
			state = STATE_MIDDLE;
			break;
		case 60:
			state = STATE_END;
			break;
		case 100:
			state = STATE_COMPLETE;
			break;
		default:
			break;
		}
	}

	private void sendProgressToHandler() {
		Message msg = handler.obtainMessage();
		msg.arg1 = progress;
		msg.arg2 = state;
		handler.sendMessage(msg);
	}
}


0
komentarzy

Przewijanie do góry
503 Service Unavailable

Service Unavailable

The server is temporarily unable to service your request due to maintenance downtime or capacity problems. Please try again later.

Additionally, a 503 Service Unavailable error was encountered while trying to use an ErrorDocument to handle the request.