Lekcja 13

Wątki II

Dowiązywanie wartości to mechanika, która pozwala na szybkie programowanie zależności w widokach. Dziś połączymy to z wykorzystaniem klas obsługujące współbieżność w ramach JavaFX.

I. Dowiązywanie wartości


Zadanie 0

Chcemy pokazać tekst w Label, który jest wpisany w TextField. Nie użyjemy ani zdarzeń, ani przycisku.


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>

<AnchorPane
    prefHeight="160.0"
    prefWidth="644.0"
    xmlns="http://javafx.com/javafx/8.0.121"
    xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="sample.Controller">

    <TextField
        fx:id="textField"
        layoutX="14.0"
        layoutY="14.0" />

    <Label
        fx:id="label"
        layoutX="195.0"
        layoutY="19.0" />

</AnchorPane>


import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;

public class Controller {

    @FXML
    private TextField textField;

    @FXML
    private Label label;

    public void initialize() {
        label.textProperty().bind(textField.textProperty());
    }

}
                       
                    

Uruchom kod, zwróć uwagę na użycie bind. Co do czego jest łączone? Czy można odwrócić napis?

Zadanie 1 (do domu, do przećwiczenia, nie na ocenę)

Napisz aplikację, która wykorzysta binding w ten sposób, aby:

  • tylko za pomocą suwaków
  • pozwala na zmianę położenia punktu startu i końca linii (Linia jako javafx.scene.shape.Line)
Zadanie na dowiązania

II. Przygotowanie sceny


Zadanie 2

Przygotuj widok w swojej aplikacji:


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ProgressBar?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Text?>

<AnchorPane
    xmlns:fx="http://javafx.com/fxml/1"
    prefHeight="196.0"
    prefWidth="969.0"
    xmlns="http://javafx.com/javafx/8.0.121"
    fx:controller="Controller">

    <Text
        fx:id="name"
        layoutX="27.0"
        layoutY="43.0"
        strokeType="OUTSIDE"
        strokeWidth="0.0"
        text="Postęp"
        wrappingWidth="210" />
    
    <ProgressBar
        fx:id="progress"
        layoutX="25.0"
        layoutY="126.0"
        prefHeight="20.0"
        prefWidth="958.0"
        progress="0.0"
        AnchorPane.leftAnchor="25.0"
        AnchorPane.rightAnchor="25.0" />

</AnchorPane>
Zadanie 3

Klasa Task pozwala nam na stworzenie takiego zadania, które będzie wykonane raz (nie można wznowić). Takie opakowanie sprawia, że JavaFX może synchronizować swoją pracę.


import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.ProgressBar;
import javafx.scene.text.Text;

public class Controller {

    // Słowo kluczowe final sprawia,
    // że nie można zmienić wartości zmiennej.
    public static final int VALUTE_TO_COMPLETE = 1000;

    @FXML
    private Text name;

    @FXML
    private ProgressBar progress;

    @FXML
    public void initialize() {

        // Tworzymy zadanie
        Task progressTask = new Task() {
            @Override
            protected Integer call() throws Exception {
                int value = 0;
                while (value < VALUTE_TO_COMPLETE) {
                    value++;
                    updateMessage("Osiągnięta wartość: " + value);
                    updateProgress(value, VALUTE_TO_COMPLETE);
                    Thread.sleep(100);
                }
                return value;
            }
        };

        // Dowiązujemy wartości
        progress.progressProperty().bind(progressTask.progressProperty());
        name.textProperty().bind(progressTask.messageProperty());

        // Uruchamiamy
        Thread thread = new Thread(progressTask);
        thread.setDaemon(true);
        thread.start();

    }

}

Uruchom kod, a następnie przenanalizuj.

III. Zatrzymanie zadania, gdy tego chcemy


Zadanie 4

Przygotuj widok w swojej aplikacji:


<Button
    layoutX="712.0"
    layoutY="25.0"
    minHeight="77.0"
    minWidth="238.0"
    mnemonicParsing="false"
    onAction="#stop"
    prefHeight="77.0"
    prefWidth="238.0"
    text="Zatrzymaj" />
Zadanie 5

Klasa Task pozwala na sprawdzenie, czy nasze zadanie nie osiągnęło stanu "Zakończone" (Cancelled). W takim wypadku, musimy zakończyć dalsze obliczenia. Nie możemy jednak zapomnieć, że musimy skończyć, gdy tylko osiągnięmy wartość maksymalną.



// Sprawdzamy w pętli, czy nasze zadanie nie zostało
// z zewnątrz przerwane
Task<Integer> progressTask = new Task<Integer>() {
    @Override
    protected Integer call() throws Exception {
        int value = 0;
        while (!isCancelled()) {
            value++;
            updateMessage("Osiągnięta wartość: " + value);
            updateProgress(value, VALUTE_TO_COMPLETE);
            Thread.sleep(100);
            if (value >= VALUTE_TO_COMPLETE) return value;
        }
        return value;
    }
};

// Dodajemy metodę, która po zatrzyma nam task.
@FXML
public void stop() {
    progressTask.cancel();
}

Przeanalizuj przedstawiony kod. Skopiuj 3 fragmenty kodu tak, aby dało radę wymusić zakończenie zadania. Pamiętaj o widoczności zmiennych.

Zadanie 6

Napisz aplikację "Boom", która składa się z:

  • Licznika czasu, który pokazuje pozostały czas
  • Postępującego paska postępu
  • Łamigłówki, którą trzeba złamać.

Jeśli użytkownik rozwiąże problem, to odliczanie zatrzymuje się.

Przykład: aplikacja losuje liczbę z dowolnego zakresu. Użytkownik ma za zadanie ją zgadnąć. Jeśli użytkownik spróbuje podać wartość i mu się nie uda, to aplikacja pokaże mu komunikat "za mało" albo "za dużo".