exercício javaFx

Tópicos: máquina de estados, interface gráfica, threads,

o diagrama da máquina de estados:

o projeto:

o código:

package jfxsomajogo;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import jfxsomajogo.modelo.SomaJogoObs;
import jfxsomajogo.ui.SomaUI;

public class JFXSomaJogo extends Application {

    public static void main(String[] args) {
        launch(args);;
    }
    
    @Override
    public void start(Stage primaryStage){
        primaryStage.setTitle("Acereta nos números");
        SomaJogoObs a = new SomaJogoObs();
        SomaUI ui = new SomaUI(a);
        Scene scene = new Scene(ui, 300,250);
        primaryStage.setScene(scene);
        primaryStage.show();
     
        //que serve para fechar a janela
        primaryStage.setOnHidden((WindowEvent event) -> {
            a.paraRelogio();
        });
    }
    
}
package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaNomeJogador extends SomaFSMAdapter {

    public AguardaNomeJogador(SomaDados dados) {
        super(dados);
    }

    //transição
    @Override
    public ISomaFSM defineJogador(String nome) {
        dados.defineNomeJogador(nome);
        if (dados.jogadorValido()) {
            dados.reset(); //iniciação do jogo
            return new AguardaResposta(dados); //novo estado
        }else{
            return this;
        }
    }

    //cada um retorna o seu próprio identicador
    //para evitar o uso do instanceof e o get class
    @Override
    public EstadoID obtemEstadoID() {
        return EstadoID.AGUARDA_NOME;
    }

}

package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaReinicio extends SomaFSMAdapter {

    public AguardaReinicio(SomaDados dados) {
        super(dados);
    }

    //este estado é basicamente, um "press any key to conitnue"
    @Override
    public ISomaFSM recomeca() {
        return new AguardaNomeJogador(dados);
    }

    @Override
    public EstadoID obtemEstadoID() {
        return EstadoID.AGUARDA_REINICIO;
    }
}

package jfxsomajogo.fsm;

import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.modelo.SomaDados;

public class AguardaResposta extends SomaFSMAdapter{

    AguardaResposta(SomaDados dados) {
        super(dados);
    }

    @Override
    public ISomaFSM tentaResposta(int n) {
        dados.tentaResposta(n);
        if(dados.excedeuErros()){
            return new AguardaReinicio(dados); //muda de estado
        }else{
            return this; //fica no mesmo estado
            //return new AguardaResposta(dados); //alternativa, mostramos evolução do estado
        }
    }
    
    @Override
    public ISomaFSM relogioAvanca() {
        dados.diminuiCountDown();
        if(dados.excedeuErros()){
            return new AguardaReinicio(dados);
        }else{
            return this;
        }
    }

    @Override
    public EstadoID obtemEstadoID(){
        return EstadoID.AGUARDA_RESPOSTA; //para evitar o instance of
    }
}

package jfxsomajogo.fsm;

import java.io.Serializable;
import jfxsomajogo.integracao.EstadoID;

public interface ISomaFSM extends Serializable{
    //vai servir para definir um estado, transições:
    default ISomaFSM defineJogador(String nome){return this;} 
    //implementação default, pode ser no Adpater
    default ISomaFSM tentaResposta(int n){return this;}
    default ISomaFSM recomeca(){return this;}
    default ISomaFSM relogioAvanca(){return this;}
    EstadoID obtemEstadoID(); //retorna um ID
    
    //default, permite a implementação default mas devia ser no adaptar e não aqui.
    //mudar no final para ver como fica
}

package jfxsomajogo.fsm;

import jfxsomajogo.modelo.SomaDados;

public abstract class SomaFSMAdapter implements ISomaFSM{
    //guarda a referencia para o modelo de dados
    protected final SomaDados dados;
    //protected para os outros estados poderem aceder à informação
    
    protected SomaFSMAdapter(SomaDados dados){
        this.dados = dados;
    }
    
    //public AdividnhaDados obtemDados(){return dados;}
    //usa-se protected para isto
    
    //implementações de default de cada um dos métodos de 
    
}


package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface


import jfxsomajogo.fsm.AguardaReinicio;

public enum EstadoID {
    //os ID dos estados
    AGUARDA_NOME, AGUARDA_RESPOSTA, AGUARDA_REINICIO
}

//este package agrupa as enumerações e constantes relacioandos com
//integração entre vários módulos da aplicação
//assim, a UD nao tem que improtar coisas dos packages de dados ou FSM
package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface

public enum MsgCode {
    START, RIGHT, WRONG, TIMEOUT
}

package jfxsomajogo.integracao;
//isto é um package que vai ser usado pelo modelo de dados e pela interface

public enum PropsID {
    
    //podiam ser usadas constantes
    //"" -> alteração gráfica
    PROP_ESTADO("prop_estado"),
    PROP_DESAFIO("prop_desafio"),
    PROP_RELOGIO("prop_relogio");
    
    String valor;

    private PropsID(String v) {
        this.valor = v;
    }

    @Override
    public String toString() {
        return valor;
    }
}

package jfxsomajogo.modelo;

import java.io.Serializable;
import java.util.Random;
import jfxsomajogo.integracao.MsgCode;

public class SomaDados implements Serializable {

    private static final int COUNTDOWN_TIMEOUT = 10;
    private static final int MAX_ERROS = 3;
    private static final int MIN_VALOR = 1;
    private static final int MAX_VALOR = 100;

    private String jogador = "X";
    private MsgCode msg; //ver o estado actual
    private int pontuacao, erros;
    private int numA, numB;
    private int countdown;
    Random rand;

    SomaDados() {
        rand = new Random();
        jogador = "X";
        msg = MsgCode.START;
        pontuacao = erros = numA = numB = -1;
        countdown = -1;

    }

    public final void reset() {
        pontuacao = 0;
        erros = 0;
        novoDesafio();
        msg = MsgCode.START;
    }

    public void defineNomeJogador(String nome) {
        jogador = nome;
    }

    public void novoDesafio() {
        numA = rand.nextInt(MAX_VALOR - MIN_VALOR + 1) + MIN_VALOR;
        numB = rand.nextInt(MAX_VALOR - MIN_VALOR + 1) + MIN_VALOR;
        resetCountDown();
    }

    public void diminuiCountDown() {
        --countdown;
        if (countdown <= 0) {
            msg = MsgCode.TIMEOUT;
            erros++;
            novoDesafio();
        }
    }

    public void tentaResposta(int resp) {
        if (resp == (numA + numB)) {
            pontuacao++;
            msg = MsgCode.RIGHT;
            novoDesafio();
        } else {
            erros++;
            msg = MsgCode.WRONG;
        }
    }

    //para ver a evolução dos estados
    public boolean jogadorValido() {
        //se indicar o nome, pode ir para jogar
        return jogador != null && jogador.length() > 0;
    }

    public boolean excedeuErros() {
        return erros >= MAX_ERROS;
    }

    //obtem info para o ecrã
    public String obtemNomeJogador() {
        return jogador;
    }

    public int obtemNumA() {
        return numA;
    }

    public int obtemNumB() {
        return numB;
    }

    public int obtemPontuacao() {
        return pontuacao;
    }

    public int obtemErros() {
        return erros;
    }

    public int obtemCountDown() {
        return countdown;
    }

    public MsgCode obtemMsg() {
        return msg;
    }

    private void resetCountDown() {
        countdown = COUNTDOWN_TIMEOUT;
    }
}

package jfxsomajogo.modelo;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import jfxsomajogo.fsm.AguardaNomeJogador;
import jfxsomajogo.fsm.ISomaFSM;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;

//esta é a classe "contexto" da máquina de estados
//tem a máquina de estados + dados
//máquina de estados
public class SomaJogo implements Serializable {

    private final SomaDados dados = new SomaDados(); //criar o modelo de dados
    private ISomaFSM estado = null;  //define o estado incial

    public SomaJogo() {
        estado = new AguardaNomeJogador(dados);
    }

    //defineEstado recebe o próximoe stado
    private Set<PropsID> defineEstado(ISomaFSM prox, PropsID... eventosBase) {
        //isto vai ajudar no observable, vai avisar a vista
        Set<PropsID> eventos = new HashSet<>(Set.of(eventosBase));
        ISomaFSM anterior = estado;
        estado = prox;
        if (anterior != prox) {
            eventos.add(PropsID.PROP_ESTADO);
        }
        return eventos;
    }

    //operação do sistema ("dispositivo") contexto da FSM
    //-> reencaminha para FSM
    //os métodos que permitem a transição de estados
    //ponte entre o estado, o modelo de estados e a vista
    public Set<PropsID> defineJogador(String nome) {
        //ao inves de retornar o estado pode ser um return de boolean
        //retornar Set<PropsID>, um conjunto de situações de jogo que ajuda na vista do jogo
        //para a parte gráfica
        return defineEstado(estado.defineJogador(nome));
    }

    public Set<PropsID> tentaResposta(int n) {
        return defineEstado(estado.tentaResposta(n), PropsID.PROP_DESAFIO);
        //estas duas significa que podem acontecer dois eventos, e disparar o fire de uma das duas
    }

    public Set<PropsID> recomeca() {
        return defineEstado(estado.recomeca());
    }

    public Set<PropsID> relogioAvanca() {
        return defineEstado(estado.relogioAvanca(), PropsID.PROP_RELOGIO, PropsID.PROP_DESAFIO);
    }

       
    public EstadoID obtemEstadoID() {
        return estado.obtemEstadoID();
    }

    //obtenção de dados para uso nas UI
    //informação relevanta para mostrar ao utilizador
    //-> reencaminha para dados
    
    //isto é o mesmo que os gets apenas traudizdo para obtem
    //para serem mostrados no UI do utilziador
    
    public String obtemNomeJogador() {
        return dados.obtemNomeJogador();
    }

    public int obtemNumA() {
        return dados.obtemNumA();
    }

    public int obtemNumB() {
        return dados.obtemNumB();
    }

    public int obtemPontuacao() {
        return dados.obtemPontuacao();
    }

    public int obtemErros() {
        return dados.obtemErros();
    }

    public MsgCode obtemMsg() {
        return dados.obtemMsg();
    }

    public int obtemCountDown() {
        return dados.obtemCountDown();
    }
    
    //não devemos retornar o modelo SomaDados todo
    //porque se o fizermos vamos dar hipotese a quem faz
    //a vista de alterar os dados

}

package jfxsomajogo.modelo;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;

//missao principal - adicionar o suporte para ligacao a vistas
// = property change support
//mantem a ligacao separada do contexto da FGSM e dados
//permite save/load a FSM+dados sem dependencia (referencia) a vistas
//adiciona save/load neste nivel e age sobre o modelo (contexto da FSM)
public class SomaJogoObs {

    //os propertir change support
    SomaJogo jogo = null; //a referencia para o jogo, maquina de estados
    private PropertyChangeSupport props = null;

    private final Timer relogio;
    private final Temporizador segundos;

    public SomaJogoObs() {
        jogo = new SomaJogo();
        props = new PropertyChangeSupport(jogo);

        relogio = new Timer();
        segundos = new Temporizador();
        relogio.schedule(segundos, 0, 1000);
    }

    //registar o interesse das vistas, quando uma determinada propriedade for alterada
    //
    public void registaPropertyChangeListener(PropsID prop, PropertyChangeListener listener) {
        props.addPropertyChangeListener(prop.toString(), listener);
    }

    private void disparaEventos(Set<PropsID> eventos) {
        for (var e : eventos) {
            props.firePropertyChange(e.toString(), null, null);
        }

        //alternaiva ao for
        //eventos.forEach((e) -> {
        //props.firePropertyChange(e.toString(), null, null);
        //});
    }

    public void paraRelogio() {
        segundos.cancel();
        relogio.purge(); //eliminar todas as as timer text 
        relogio.cancel();
    }

    // inicio: métodos que fazem a ponte entre as vistas e a maquina de estados
    public void relogioAvanca() {
        disparaEventos(jogo.relogioAvanca());
    }

    // operação do sistema ("dispotivo") - reencaminha para jogo depois p/ FSM
    public void defineJogador(String nome) {
        disparaEventos(jogo.defineJogador(nome));
    }

    public void tentaResposta(int n) {
        disparaEventos(jogo.tentaResposta(n));

        //alternaiva ao udo do disparaEventos
        //jogo.tentaResposta(n);
        //props.firePropertyChange(PropsID.PROP_DESAFIO, null. null);
        //props.firePropertyChange(PropsID.PROP_ESTADO, null. null);
    }

    public void recomeca() {
        disparaEventos(jogo.recomeca());
    }

    public EstadoID obtemEstadoID() {
        return jogo.obtemEstadoID();
    }
    //fim: métodos que fazem a ponte entre as vistas e a maquina de estados

    //obenção de dados para uso das UI - reencaminha p/jogo (depois para dados)
    public String obtemNomeJogador() {
        return jogo.obtemNomeJogador();
    }

    public int obtemNumA() {
        return jogo.obtemNumA();
    }

    public int obtemNumB() {
        return jogo.obtemNumB();
    }

    public int obtemPontuacao() {
        return jogo.obtemPontuacao();
    }

    public int obtemErros() {
        return jogo.obtemErros();
    }

    public MsgCode obtemMSG() {
        return jogo.obtemMsg();
    }

    public int obtemCountDown() {
        return jogo.obtemCountDown();
    }

    //load/save
    public void saveGame(String filename) {
        ObjectOutputStream out;
        try {
            out = new ObjectOutputStream(new FileOutputStream(filename));
            out.writeObject(jogo);
            out.close();
        } catch (IOException e) {
            Logger.getLogger(SomaJogoObs.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    public void loadGame(String filename) {
        ObjectInputStream in;
        try {
            in = new ObjectInputStream(new FileInputStream(filename));
            jogo = (SomaJogo) in.readObject();
            in.close();
            props.firePropertyChange(PropsID.PROP_ESTADO.toString(), null, null);
        } catch (IOException | ClassNotFoundException e) {
            Logger.getLogger(SomaJogoObs.class.getName()).log(Level.SEVERE, null, e);
        }
    }

    //time taks
    class Temporizador extends TimerTask {
        @Override
        public void run() {
            relogioAvanca();
        }
    }
}

package jfxsomajogo.ui;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import jfxsomajogo.integracao.EstadoID;
import jfxsomajogo.integracao.MsgCode;
import jfxsomajogo.integracao.PropsID;
import jfxsomajogo.modelo.SomaJogoObs;


//esta é uma classe genérica, que é um adpater para PropertyChangeListener
//PropertyChangeListenerJFXAdapter faz com que seja implementado o PropertyChangeListener
//vai implementar o propertyChange, que é o método obrigatório pelo inteface método PropertyChangeListener
//e sempre que ele for chamado, e antes de correr o código pretendido ele faz um  
// Platform.runLater(() do método onChange(evt);
// e quanto ele for corrido ele já foi corrido no contexto do .runLater(()
//com o objecto qd é criado é abstracto 
//por exemplo propsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() 
//sou obrigado a implementar o onChange(), com todo o código necessário
//para as alterações que eu necessito 
//e assim o onChange() já foi executado na thread do javaFX
//evito assim que estoire, e que acontece quanto tentamos executar código a partir de uma thread
//difernte da que está responsavel por fazer a gestão dos componentes gráficos
//isto por causa da utilização do timer, porque o timer está numa thread à parte


abstract class PropertyChangeListenerJFXAdapter implements PropertyChangeListener{
    @Override
    public final void propertyChange(PropertyChangeEvent evt){
        Platform.runLater(()->{
            onChange(evt);
        });
    }
abstract public void onChange(PropertyChangeEvent evt);
}


public class SomaUI extends BorderPane {

    SomaJogoObs adivObs;

    public SomaUI(SomaJogoObs a) {
        adivObs = a;
        FileChooser fileChooser = new FileChooser();

        //esta funcao esta extensa. passar código para funcções auxiliares
        //implica passar as refs locais dos componentes para a classe
        //cria componentes
        //menu
        MenuBar menuBar = new MenuBar();

        Menu fileMenu = new Menu("file");
        MenuItem fileNew = new MenuItem("new");
        MenuItem fileSave = new MenuItem("save");
        MenuItem fileLoad = new MenuItem("load");
        MenuItem fileExit = new MenuItem("exit");
        fileMenu.getItems().addAll(fileNew, fileSave, fileLoad, fileExit);

        Menu aboutMenu = new Menu("about");
        MenuItem aboutInfo = new MenuItem("info");
        aboutMenu.getItems().addAll(aboutInfo);

        menuBar.getMenus().addAll(fileMenu, aboutMenu);
        setTop(menuBar);

        //eventos para os menus
        //file->new
        fileNew.setOnAction((ActionEvent e) -> {
            adivObs.recomeca();
        });
        //file-> save
        fileSave.setOnAction((ActionEvent e) -> {
            File f = fileChooser.showSaveDialog(getScene().getWindow());
            if (f != null) {
                adivObs.saveGame(f.getAbsolutePath());
            }

        });

        //file -> load
        fileLoad.setOnAction((ActionEvent e) -> {
            File f = fileChooser.showSaveDialog(getScene().getWindow());
            if (f != null) {
                adivObs.loadGame(f.getAbsolutePath());
            }

        });

        //file -> exit
        fileExit.setOnAction((ActionEvent e) -> {
            encerra();
            ((Stage) getScene().getWindow()).close();

            //Platform.exit(); //funciona apenas em windows
        });
        //about
        aboutInfo.setOnAction((ActionEvent e) -> {
            Alert alert = new Alert(Alert.AlertType.INFORMATION);
            alert.setHeaderText("acerca disto");
            alert.getDialogPane().setContentText("programa de javafx");

            alert.showAndWait();
        });

        StackPane areaUtil = new StackPane();
        areaUtil.setPadding(new Insets(10));

        UINome uinome = new UINome(); //é um pane
        UIJoga uijoga = new UIJoga(); //é um pane
        UIFim uifim = new UIFim();

        uijoga.setVisible(false);
        uifim.setVisible(false);

        areaUtil.getChildren().addAll(uinome, uijoga, uifim);

        setCenter(areaUtil);
    }

    public void encerra() {
        adivObs.paraRelogio();
    }

    class UINome extends VBox {

        private final Label lNome = new Label("escreve nome");
        private final TextField tfNome = new TextField();
        private final Button btnDefineNome = new Button("ok");

        public UINome() {
            super(10); //espaçamento entre items
            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    setVisible(adivObs.obtemEstadoID() == EstadoID.AGUARDA_NOME);
                }
            });
            btnDefineNome.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    adivObs.defineJogador(tfNome.getText());
                }
            });
            tfNome.setOnKeyPressed(e -> {
                if (e.getCode() == KeyCode.ENTER) {
                    adivObs.defineJogador(tfNome.getText());
                }
            });
            getChildren().addAll(lNome, tfNome, btnDefineNome);

        }

    }

    class UIJoga extends VBox {

        private final Label lMsg = new Label();
        private final Label lDesafio = new Label();
        private final TextField tfResposta = new TextField();
        private final Button btnEnviaResposta = new Button("ok");
        private final Label lRelogio = new Label();
        Map<MsgCode, String> mensagens = new HashMap<>();

        public UIJoga() { //1,39
            super(10); //espaçamento entre items
            mensagens.put(MsgCode.START, "vamos começar");
            mensagens.put(MsgCode.RIGHT, "certo. outro desafio");
            mensagens.put(MsgCode.WRONG, "errado. tenta outra vez");
            mensagens.put(MsgCode.TIMEOUT, "tempo excedido. outro desafio");

            btnEnviaResposta.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent t) {
                    tentaResposta();
                }
            });
            tfResposta.setOnKeyPressed(e -> {
                if (e.getCode() == KeyCode.ENTER) {
                    tentaResposta();
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    if (adivObs.obtemEstadoID() == EstadoID.AGUARDA_RESPOSTA) {
                        setVisible(true);
                        defineMensagens(); //atualziar todas as labels com o texto em causa
                        intiTfResposta(); //por o texto a vazio (escrita do texto), quando existe mudança de estado
                    } else {
                        setVisible(false);
                    }
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_DESAFIO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    defineMensagens();
                }
            });

            adivObs.registaPropertyChangeListener(PropsID.PROP_RELOGIO, new PropertyChangeListenerJFXAdapter() {
                //registaPropertyChangeListener para sermos avisados de evolução de estados
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    //para processamento futuro caso necessário
                }
            });

            getChildren().addAll(lMsg, lDesafio, tfResposta, btnEnviaResposta, lRelogio);
            defineMensagens();
        }

        private void defineMensagens() {
            lMsg.setText(mensagens.getOrDefault(adivObs.obtemMSG(), "nao compreendo.."));
            //adivObs.obtemMSG() com ajuda de um switch case para obter as mensagens (string) acerca do estado
            //em que se encontra
            //a alternativa foi a opção de se usar um Map<MsgCode, String> mensagens

            lDesafio.setText(adivObs.obtemNomeJogador()
                    + " ,quando é "
                    + adivObs.obtemNumA()
                    + " + "
                    + adivObs.obtemNumB() + "?"
            );
            lRelogio.setText("tempo restante: " + adivObs.obtemCountDown());
        }

        private void intiTfResposta() {
            //serve para apagar a resposta que é escrita
            //e só acontece quando existe mudança de estado
            tfResposta.setText("");
            tfResposta.requestFocus();

        }

        private void tentaResposta() {
            try {
                int val = Integer.parseInt(tfResposta.getText());
                adivObs.tentaResposta(val);
                intiTfResposta();

            } catch (NumberFormatException e) {

            }
        }
    }

    class UIFim extends VBox {

        private final Label lResultado = new Label();
        private final Button btnRecomecar = new Button("Novo jogo");

        public UIFim() {
            super(5);
            adivObs.registaPropertyChangeListener(PropsID.PROP_ESTADO, new PropertyChangeListenerJFXAdapter() {
                @Override
                public void onChange(PropertyChangeEvent evt) {
                    if (adivObs.obtemEstadoID() == EstadoID.AGUARDA_REINICIO) {
                        setVisible(true);
                        lResultado.setText(adivObs.obtemNomeJogador()
                                + ", o teu resultado:\n"
                                + "pontuação: "
                                + adivObs.obtemPontuacao()
                                + "\n"
                                + "Erros:" + +adivObs.obtemErros()
                                + "(obviamente)\n\n"
                                + "ok para jogar outra vez\n\n"
                        );
                        //formatação com \n em ambiente grafico - tem tudo a ver
                    } else {
                        setVisible(false);
                    }
                }
            });
            btnRecomecar.setOnAction((ActionEvent e) -> {
                adivObs.recomeca();
            }
            );
            getChildren().addAll(lResultado, btnRecomecar);
        }

    }

}

Tags : ,

0 thoughts on “exercício javaFx”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.