Diskuze: Jednoduchý 3 2 1 countdown - GUI zamrzne (v JavaFX)
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.
Člen
Zobrazeno 20 zpráv z 20.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí Java, jsme si ověřili nabyté zkušenosti z kurzu.
Ahoj,
Vyhodilo ti to nejakú chybu do konzoly?
Používaš FXML súbor na definovanie GUI, alebo inštancie komponentov
vytváraš v triede? Pokiaľ používaš FXML súbor, skontroluj si, či máš
premennú countdownLabel oAnotovanú @FXML anotáciou a či máš v FXML k
tomuto Labelu určené fx:id s rovnakým menom, napr:
<Label fx:id="countdownLabel />"
Ak táto rada nepomôže, hodil by sa celý kód, alebo aspoň časti, ktoré s tým môžu súvisieť.
Jednoduchá odpověď... Zamrznutí hlavního vlákna. Potřebuješ ten odpočet provést ve vlákně na pozadí, což v javafx zajišťují věci např. z packegů javafx.cuncurrent, nebo javafx.animation... A ano, nejde použít klasický Thread.
//EDIT: ten první pacage má být javafx.concurrent
Je to proto, že se pokoušíš nastavit gui prvek na nesprávném vlákně, v Java FX musíš všechno nastavovat na FX Application vlákně.
Nejsem v JavaFX taky nijak sběhlý, sám ji vůbec nepoužívám, ale myslím, že v tomhle případě bys mohl využít Timeline a binding nějak takto:
private final int countDownSeconds = 3; // bude se odpočítávat od 3 do nuly
private final StringProperty countDownValue = new SimpleStringProperty(countDownSeconds + ""); //StringProperty countDownValue se později 'přibinduje' k countdownLabel, když se změní hodnota countDownValue, změní se i countdownLabel
//nyní vytvoříme timeline s duration 1 sekunda
private final Timeline countdown = new Timeline(new KeyFrame(Duration.seconds(1), (e) -> {
int i = Integer.parseInt(countDownValue.getValue()) - 1; //koukneme jaka je hodnota countDownValue a snížíme o jedna
countDownValue.setValue(i + ""); //nastavíme countDownValue novou hodnotu
}));
někde v initialize() je třeba přibindovat tu countDownValue k countdownLabel:
countdownLabel.textProperty().bind(countDownValue);
a celé se to pak na požádání spustí tak, že někde (třeba při stisku nějakého tlačítka) provedeš:
countdown.setCycleCount(countDownSeconds); //nastaví se počet opakování, tedy pošet sekund k odpočtu
countdown.play(); //a jedeme...
Zde je celý zdrojový kód FXMLDocumentConrtoller.java
Mimochodem, programuji počítadlo pro STIGA Table Hockey.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package stiga.scoreboard;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
/**
*
* @author Vojta
*/
public class FXMLDocumentController implements Initializable {
@FXML
private Label hrac1BodyTextField;
@FXML
private Label hrac2BodyTextField;
@FXML
private Label casTextField;
@FXML
private Button pridejHrac1Button;
@FXML
private Button pridejHrac2Button;
@FXML
private Button odeberHrac1Button;
@FXML
private Button odeberHrac2Button;
@FXML
private Button startButton;
@FXML
private Label countdownLabel;
private int body1 = 0;
private int body2 = 0;
@FXML
private void handlepridejHrac1ButtonAction(ActionEvent event) {
if(body1 < 99)
{
body1 = body1 + 1;
hrac1BodyTextField.setText(String.valueOf(body1));
}
}
@FXML
private void handlepridejHrac2ButtonAction(ActionEvent event) {
if(body2 < 99)
{
body2 = body2 + 1;
hrac2BodyTextField.setText(String.valueOf(body2));
}
}
@FXML
private void handleodeberHrac1ButtonAction(ActionEvent event) {
if(body1 > 0)
{
body1 = body1 - 1;
hrac1BodyTextField.setText(String.valueOf(body1));
}
}
@FXML
private void handleodeberHrac2ButtonAction(ActionEvent event) {
if(body2 > 0)
{
body2 = body2 - 1;
hrac2BodyTextField.setText(String.valueOf(body2));
}
}
@FXML
private void handlestartButtonAction(ActionEvent event)
{
for (int i = 3; i > 0; i--)
{
countdownLabel.setText(String.valueOf(i));
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
System.err.println("Error" + ex);
}
}
}
@Override
public void initialize(URL url, ResourceBundle rb) {
}
}
Jak přesně myslíš toto?:
někde v initialize() je třeba přibindovat tu countDownValue k countdownLabel:countdownLabel.textProperty().bind(countDownValue)
Děkuji moc. Už jsem na to přišel. Poslední dva dny jsem nehledal na internetu nic jiného. Fakt moc díky
Ještě jeden dotaz, je nějaký způsob jak za běhu měnit hodnotu countDownSeconds?
private final int countDownSeconds = 3;
Zdravím, ještě jsem se chtěl zeptat, je nějaká možnost, jak celý
tento 321 countdown použít znovu?
Myslím to tak, že po zmáčknutí tlačítka by se odpočítalo 3 2 1, a
poté, kdybych zmáčknul tlačítko znovu, tak by to odpočítalo 321 ještě
jednou...
Přesuň vytváření té Timeline do metody co jí reaguješ na stisk tlačítka, tak aby se pokaždé vytvářela znova. Možná by pak bylo vhodné po odstartování toho odpočtu to tlačítko nastavit jako disabled a opětovně nastavit jako enabled po dokončení toho odpočtu, tak aby nešlo furt na něj klikat a spouštět nový a nový odpočet.
Změnil jsem kód na toto:
private int countDownSeconds = 3; // bude se odpočítávat od 3 do nuly
private StringProperty countDownValue = new SimpleStringProperty(countDownSeconds + "");
@FXML
private void handlespustitCasButtonAction(ActionEvent event)
{
countdownLabel.setVisible(true);
startSequenceSPlay();
startButton.setVisible(false);
zadatOsobyButton.setVisible(false);
Timeline countdown = new Timeline(new KeyFrame(Duration.seconds(1), (e) -> {
int i = Integer.parseInt(countDownValue.getValue()) - 1; //koukneme jaka je hodnota countDownValue a snížíme o jedna
countDownValue.setValue(i + "");//nastavíme countDownValue novou hodnotu
if (i == 0)
{
countDownValue.setValue("");
startTimeLine();
enableGoalButtons();
}
}));
countdown.setCycleCount(countDownSeconds); //nastaví se počet opakování, tedy pošet sekund k odpočtu
countdown.play(); //a jedeme...
spustitCasButton.setDisable(true);
int s = Integer.parseInt(casSekundyValue.getValue());
int m = Integer.parseInt(casMinutyValue.getValue());
if(s <= 33 && m == 0)
{
countdownSPlay();
}
goalSongStop();
Poprvé to funguje, ale když to chci pustit znovu, už to nejde a napíše
to do konzole error.
Napadlo mě, že bych celou timeline měl restartovat. Restartovat jsem to
musel, když se timer dokončí, což znamená v podmínce
if (i == 0)
ale když jsem dovnitř této podmínky umístil
countdown.stop();
aplikaci mi to vůbec nezpustilo a psalo mi to u tohoto řádku chybu:
"variable countdown might not have been initialized"
co s tím?
Ten samý error, jenom se teď přesunul k řádku
if (i == 0 && countdown != null)
a podtrhnul countdown...
Prepáč, až teraz som si všimol kde sa tá podmienka nachádza Problém je v tom, že v čase, kedy sa môže vykonať podmienka
if(i == 0)
Do countdown ešte nie je priradená žiadna hodnota, pretože tá podmienka sa nachádza pri vytváraní KeyFrame ktoré je parameter pre konštruktor triedy Timeline
ehm... jak to ale vyřešit?
S animáciami som v JavaFX nepracoval, takže nájsť problém je jediné čo som pre Teba zatiaľ mohol spraviť Ale kým ti tu neodpíše niekto zdatnejší, mysím si, že metódu stop() by nemal volať agregovaný objekt.
Zobrazeno 20 zpráv z 20.