NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

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.

Aktivity
Avatar
Vojtěch Bešťák:20.5.2016 20:02

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.5.2016 20:02
Avatar
Peter T
Člen
Avatar
Peter T:20.5.2016 21:05

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.5.2016 21:05
Avatar
pocitac770
Tvůrce
Avatar
Odpovídá na Vojtěch Bešťák
pocitac770:20.5.2016 21:31

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.5.2016 21:31
Avatar
pocitac770
Tvůrce
Avatar
pocitac770:20.5.2016 21:36

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

 
Nahoru Odpovědět
20.5.2016 21:36
Avatar
Atrament
Člen IT Redactor Gang
Avatar
Odpovídá na Vojtěch Bešťák
Atrament:20.5.2016 21:42

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í
+2,50 Kč
Řešení problému
 
Nahoru Odpovědět
20.5.2016 21:42
Avatar
Odpovídá na Peter T
Vojtěch Bešťák:20.5.2016 22:21

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.5.2016 22:21
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:20.5.2016 22:23

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.5.2016 22:23
Avatar
Atrament
Člen IT Redactor Gang
Avatar
Atrament:20.5.2016 22:29

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

 
Nahoru Odpovědět
20.5.2016 22:29
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:20.5.2016 22:32

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.5.2016 22:32
Avatar
Vojtěch Bešťák:20.5.2016 23:06

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.5.2016 23:06
Avatar
Atrament
Člen IT Redactor Gang
Avatar
Atrament:20.5.2016 23:21

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.5.2016 23:21
Avatar
Odpovídá na Atrament
Vojtěch Bešťák:22.5.2016 13:46

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.5.2016 13:46
Avatar
Atrament
Člen IT Redactor Gang
Avatar
Odpovídá na Vojtěch Bešťák
Atrament:22.5.2016 19:29

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.5.2016 19:29
Avatar
Vojtěch Bešťák:22.5.2016 20:29

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.5.2016 20:29
Avatar
Peter T
Člen
Avatar
Peter T:22.5.2016 20:46

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.5.2016 20:49
 
Nahoru Odpovědět
22.5.2016 20:46
Avatar
Vojtěch Bešťák:22.5.2016 20:53

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

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

a podtrhnul countdown... :D

 
Nahoru Odpovědět
22.5.2016 20:53
Avatar
Peter T
Člen
Avatar
Odpovídá na Peter T
Peter T:22.5.2016 20:54

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.5.2016 20:54
Avatar
Vojtěch Bešťák:22.5.2016 20:56

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

 
Nahoru Odpovědět
22.5.2016 20:56
Avatar
Odpovídá na Peter T
Vojtěch Bešťák:22.5.2016 21:28

Peter T ?

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

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