Diskuze: Jednoduchý 3 2 1 countdown - GUI zamrzne (v JavaFX)

Java Java Jednoduchý 3 2 1 countdown - GUI zamrzne (v JavaFX)

Avatar
Vojtěch Bešťák:

Ahoj, Doufám, že neduplikuji otázku, ale nemohl jsem najít takovou otázku, která by vystihovala můj problém. Programuji jednoduchá 3 2 1 countdown. Nejdříve jsem udělal, aby se countdown vypisoval do konzole. To fungovalo vpořádku.

for (int i = 3; i > 0; i--)
{
    System.out.println(i);
    try {
            Thread.sleep(1000);
    } catch (InterruptedException ex) {
            System.err.println("Error" + ex);
    }
}

Ale když jsem zaměnil toto

System.out.println(i);

tímto

countdownLabel.setText(String.valueOf(i));

a kliknul jsem na tlačítko v mojí aplikaci, které to spoušťí, pouze to zmrazilo celé okno na 3 sekundy.

Prosím pomozte mi, už jsem prohledal celý intertnet, ale nenalezl jsem, to co jsem potřeboval. A když už ano, tak to nefungovalo.

Co mám dělat?
Btw: Nejsem moc zdatný programátor se JavaFX teprve začínám...

 
Odpovědět 20. května 20:02
Avatar
Peter T
Člen
Avatar
Peter T:

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ť.

 
Nahoru Odpovědět 20. května 21:05
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Vojtěch Bešťák
pocitac770:

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.

 
Nahoru Odpovědět 20. května 21:31
Avatar
pocitac770
Redaktor
Avatar
pocitac770:

//EDIT: ten první pacage má být javafx.concurrent

 
Nahoru Odpovědět 20. května 21:36
Avatar
Atrament
Člen
Avatar
Odpovídá na Vojtěch Bešťák
Atrament:

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...
Akceptované řešení
+20 Zkušeností
+1 bodů
Řešení problému
 
Nahoru Odpovědět 20. května 21:42
Avatar
Odpovídá na Peter T
Vojtěch Bešťák:

Zde je celý zdrojový kód FXMLDocumentCon­rtoller.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) {

    }



}
 
Nahoru Odpovědět 20. května 22:21
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:

Jak přesně myslíš toto?:

někde v initialize() je třeba přibindovat tu countDownValue k countdownLabel:cou­ntdownLabel.tex­tProperty().bin­d(countDownVa­lue)

 
Nahoru Odpovědět 20. května 22:23
Avatar
Atrament
Člen
Avatar
Atrament:

Poslední tři řádky zdrojáku co jsi tu před chvíli postnul :)

 
Nahoru Odpovědět 20. května 22:29
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:

Děkuji moc. Už jsem na to přišel. Poslední dva dny jsem nehledal na internetu nic jiného. Fakt moc díky ;)

 
Nahoru Odpovědět 20. května 22:32
Avatar
Vojtěch Bešťák:

Ještě jeden dotaz, je nějaký způsob jak za běhu měnit hodnotu countDownSeconds?

private final int countDownSeconds = 3;
 
Nahoru Odpovědět 20. května 23:06
Avatar
Atrament
Člen
Avatar
Atrament:

Jo je, nedělej ji final a pak si ji můžeš libovolně přenastavit kdy se ti zlíbí.

 
Nahoru Odpovědět 20. května 23:21
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:

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...

 
Nahoru Odpovědět 22. května 13:46
Avatar
Atrament
Člen
Avatar
Odpovídá na Vojtěch Bešťák
Atrament:

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.

 
Nahoru Odpovědět 22. května 19:29
Avatar
Vojtěch Bešťák:

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? :D

 
Nahoru Odpovědět 22. května 20:29
Avatar
Peter T
Člen
Avatar
Peter T:

Znamená to, že môže nastať prípad, že countdown == NULL, podmienku uprav takto:

if(i == 0 && countdown != NULL)  {
        countdown.stop();
}

A malo by to ísť :)

Editováno 22. května 20:49
 
Nahoru Odpovědět 22. května 20:46
Avatar
Vojtěch Bešťák:

Ten samý error, jenom se teď přesunul k řádku

if (i == 0 && countdown != null)

a podtrhnul countdown... :D

 
Nahoru Odpovědět 22. května 20:53
Avatar
Peter T
Člen
Avatar
Odpovídá na Peter T
Peter T:

Prepáč, až teraz som si všimol kde sa tá podmienka nachádza :D 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

 
Nahoru Odpovědět 22. května 20:54
Avatar
Vojtěch Bešťák:

ehm... jak to ale vyřešit? :D

 
Nahoru Odpovědět 22. května 20:56
Avatar
Odpovídá na Peter T
Vojtěch Bešťák:

Peter T ?

Editováno 22. května 21:29
 
Nahoru Odpovědět 22. května 21:28
Avatar
Peter T
Člen
Avatar
Odpovídá na Vojtěch Bešťák
Peter T:

S animáciami som v JavaFX nepracoval, takže nájsť problém je jediné čo som pre Teba zatiaľ mohol spraviť :D Ale kým ti tu neodpíše niekto zdatnejší, mysím si, že metódu stop() by nemal volať agregovaný objekt.

 
Nahoru Odpovědět 22. května 21:56
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 20 zpráv z 20.