Tag: javafx
JavaFX, exercício 3.1
package recursopa; import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; import recursopa.iu.gui.PaneOrganizer; public class RecursoPA extends Application { public static void main(String[] args) { //passamos os argumentos para o subsistema gráfico os parametros launch(args); } @Override //quando começa a aplicação public void start(Stage stage) throws Exception{ PaneOrganizer po = new PaneOrganizer(); Scene scene = new Scene(po,600,400); stage.setScene(scene); //aplica a cena no stage stage.setTitle("Exercicio de programação avançada"); stage.setMinHeight(300.0); stage.setMinWidth(300.0); stage.show(); } }
package recursopa.iu.gui; import javafx.event.EventHandler; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.input.MouseEvent; import javafx.scene.paint.Color; import recursopa.logica.Desenho; import recursopa.logica.Figura; public class AreaDesenho extends Canvas { Desenho desenho; public AreaDesenho(Desenho desenho) { super(100.0, 100.0); this.desenho = desenho; } public void registaListeners() { //detectar as ações do rato this.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent t) { desenho.iniciaFigura((int) t.getX(), (int) t.getY(), (int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256) ); atualiza(); } }); //usando lambda functions this.setOnMouseReleased((t) -> { desenho.atualizaFigura((int) t.getX(), (int) t.getY()); atualiza(); }); this.setOnMouseDragged((t) -> { desenho.finalizaFigura((int) t.getX(), (int) t.getY()); atualiza(); }); } void apagarCanvas(GraphicsContext gc) { gc.setFill(Color.rgb(233, 233, 0)); gc.fillRect(0, 0, getWidth(), getHeight()); } //para desenhar as figuras void atualiza() { //obter as ferramentas de desenho GraphicsContext gc = getGraphicsContext2D(); apagarCanvas(gc); //rir buscar todas as figuras for (Figura fig : desenho.getListaFiguras()) { //fazer as operações sobre o GraphicsContext Color cor = Color.rgb(fig.getR(), fig.getG(), fig.getB()); gc.setFill(cor); gc.fillOval(fig.getXi(), fig.getYi(), fig.getLargura(), fig.getAltura()); } //desenhar a figura actual Figura figura = desenho.getActual(); if (figura != null){ Color cor = Color.rgb(figura.getR(), figura.getG(), figura.getB()); gc.setFill(cor); gc.fillOval(figura.getXi(), figura.getYi(), figura.getLargura(), figura.getAltura()); } } public void setDim(int w, int h) { setWidth(w); setHeight(h); atualiza(); } }
package recursopa.iu.gui; import javafx.scene.layout.Pane; import recursopa.logica.Desenho; public class PaneOrganizer extends Pane { //vai extender sempre um dos containers possiveis, extends Pane Desenho desenho; //Canvas canvas; //é uma zona de desenho AreaDesenho areaDesenho; public PaneOrganizer() { desenho = new Desenho(); criaLayout(); //para criar os componentes que vão surgir no ecrã registaListeners(); // registar os comportamentos dos objectos } void criaLayout() { //adicionar o elemento ao Pane areaDesenho = new AreaDesenho(desenho); this.getChildren().add(areaDesenho); } private void registaListeners() { areaDesenho.registaListeners(); this.widthProperty().addListener((e) -> { areaDesenho.setDim((int)PaneOrganizer.this.getWidth(), (int)PaneOrganizer.this.getHeight()); }); this.heightProperty().addListener((e) -> { areaDesenho.setDim((int)PaneOrganizer.this.getWidth(), (int)PaneOrganizer.this.getHeight()); }); } }
package recursopa.logica; import java.util.ArrayList; import java.util.List; public class Desenho { //para um conjunto de figuras, que ser´auma lista de figuras List<Figura> listaDeFiguras; Figura figuraAtual; public Desenho() { listaDeFiguras = new ArrayList<>(); } public void iniciaFigura(int x, int y, int r, int g, int b) { figuraAtual = new Figura(); figuraAtual.setRGB(r, g, b); figuraAtual.setPrimeiroPonto(x, y); figuraAtual.setSegundoPonto(x, y); } public void atualizaFigura(int x, int y) { if (figuraAtual != null) { figuraAtual.setSegundoPonto(x, y); //atualizar o segundo ponto } } public void finalizaFigura(int x, int y) { //para terminar a figura if(figuraAtual != null && listaDeFiguras !=null){ listaDeFiguras.add(figuraAtual); figuraAtual = null; } } public Figura getActual(){ return figuraAtual; } public List<Figura> getListaFiguras(){ return listaDeFiguras; } }
package recursopa.logica; public class Figura { //métodos necessários para gerir a figura int xi, yi, xf, yf; //posicao e tamanho int r, g, b; //para a cor public Figura() { } public int getXi() { return xi < xf ? xi : xf; } public int getYi() { return yi < yf ? yi : yf; } public int getXf() { return xf < xi ? xf : xi; } public int getYf() { return yf < yi ? yf : yi; } public int getR() { return r; } public int getG() { return g; } public int getB() { return b; } public void setRGB(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public void setPrimeiroPonto(int x, int y) { this.xi = x; this.yi = y; } public void setSegundoPonto(int x, int y) { this.xf = x; this.yf = y; } public int getLargura() { return Math.abs(xf - xi); //não interessa a ordem dos valores } public int getAltura() { return Math.abs(yf - yi); //não interessa a ordem dos valores } }
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); } } }
JavaFX, exercício 3
package recursopa; import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; import recursopa.iu.gui.PaneOrganizer; public class RecursoPA extends Application { public static void main(String[] args) { //passamos os argumentos para o subsistema gráfico os parametros launch(args); } @Override //quando começa a aplicação public void start(Stage stage) throws Exception{ PaneOrganizer po = new PaneOrganizer(); Scene scene = new Scene(po,600,400); stage.setScene(scene); //aplica a cena no stage stage.setTitle("Exercicio de programação avançada"); stage.setMinHeight(300.0); stage.setMinWidth(300.0); stage.show(); } }
package recursopa.iu.gui; import javafx.event.EventHandler; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Ellipse; import recursopa.logica.Figura; public class PaneOrganizer extends Pane { //vai extender sempre um dos containers possiveis, extends Pane Figura fig; Ellipse elipse; public PaneOrganizer() { fig = new Figura(); fig.setPrimeiroPonto(0, 0); fig.setSegundoPonto(100, 50); fig.setRGB(0, 50, 0); criaLayout(); //para criar os componentes que vão surgir no ecrã registaListeners(); // registar os comportamentos dos objectos atualizaElipse(); } void criaLayout() { //adicionar o elemento ao Pane elipse = new Ellipse(); this.getChildren().add(elipse); } public void registaListeners() { //detectar as ações do rato this.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent t) { fig.setPrimeiroPonto((int) t.getX(), (int) t.getY()); fig.setSegundoPonto((int) t.getX(), (int) t.getY()); fig.setRGB((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); atualizaElipse(); } }); //usando lambda functions this.setOnMouseReleased((t) -> { fig.setSegundoPonto((int) t.getX(), (int) t.getY()); atualizaElipse(); }); this.setOnMouseDragged((t) -> { fig.setSegundoPonto((int) t.getX(), (int) t.getY()); atualizaElipse(); }); } //para desenhar a elipse void atualizaElipse() { //elipse.setFill(Color.GREEN); elipse.setFill(Color.rgb(fig.getR(), fig.getG(), fig.getB())); //para ir buscar as informações à fig //elipse.setCenterX(200.0); elipse.setCenterX((fig.getXi() + fig.getXf()) / 2); //elipse.setCenterY(200.0); elipse.setCenterY((fig.getYi() + fig.getYf()) / 2); //elipse.setRadiusX(150.0); elipse.setRadiusX(fig.getLargura() / 2); //elipse.setRadiusY(100.0); elipse.setRadiusY(fig.getAltura() / 2); } }
package recursopa.logica; public class Figura { //métodos necessários para gerir a figura int xi, yi, xf, yf; //posicao e tamanho int r, g, b; //para a cor public Figura() { } public int getXi() { return xi; } public int getYi() { return yi; } public int getXf() { return xf; } public int getYf() { return yf; } public int getR() { return r; } public int getG() { return g; } public int getB() { return b; } public void setRGB(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public void setPrimeiroPonto(int x, int y) { this.xi = x; this.yi = y; } public void setSegundoPonto(int x, int y) { this.xf = x; this.yf = y; } public int getLargura() { return Math.abs(xf - xi); //não interessa a ordem dos valores } public int getAltura() { return Math.abs(yf - yi); //não interessa a ordem dos valores } }
JavaFX, exercícios
Relembrar para o netBeans correr um projeto com JavaFX e nas propriedades de um projeto:
import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class RecursoPA extends Application { public static void main(String[] args) { //passamos os argumentos para o subsistema gráfico os parametros launch(args); } @Override //quando começa a aplicação public void start(Stage stage) throws Exception{ BorderPane bp = new BorderPane(); Label label1 = new Label("programação avançada"); Label label2 = new Label("outra label"); bp.setBottom(label1); bp.setTop(label2); //criar a cena Scene scene = new Scene(bp,400,400); //raiz da cena, e dimensões //adicionar a cena ao palco stage.setScene(scene); stage.show(); } }
JavaFX, exercício 2
package javafx03; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import javafx03.iu.gui.PaneOrganizer; public class Javafx03 extends Application { public static void main(String[] args) { //redirecionar a execução para o método launch launch(args); } @Override public void init() throws Exception { //inicar qualquer coisa antes do start System.out.println("init"); super.init(); } //méotod obrigatório abstract do Application @Override public void start(Stage stage) throws Exception { System.out.println("start"); //no palco vão ocorrer cenas PaneOrganizer po = new PaneOrganizer(); Scene cena = new Scene(po, 600, 400); stage.setScene(cena); stage.setTitle("programação avançada!"); stage.setMinWidth(300); stage.setMinHeight(200); stage.show(); } @Override public void stop() throws Exception { System.out.println("stop"); super.stop(); } }package javafx03.iu.gui; import javafx.event.EventHandler; import javafx.scene.input.MouseEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.scene.shape.Ellipse; import javafx03.logica.Figura; //pane, usa o espaço todo disponivel public class PaneOrganizer extends Pane { //criar a figura Figura figura; Ellipse elipse; public PaneOrganizer() { this.figura = new Figura(); figura.setPrimeiroPonto(0, 0); figura.setSegundoPonto(100, 50); figura.setRGB(0, 255, 0); criarLayout(); registaListeners(); atualizaFigura(); } private void criarLayout() { //criar uma nova elipse /* elipse.setCenterX(200); elipse.setCenterY(100); elipse.setFill(Color.GREEN); elipse.setRadiusX(200); elipse.setRadiusY(100); */ //adicionar a elipse ao pane elipse = new Ellipse(); this.getChildren().add(elipse); } private void registaListeners() { //podia ser feito numa classe à parte this.setOnMousePressed(new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent t) { figura.setPrimeiroPonto((int) t.getX(), (int) t.getY()); figura.setSegundoPonto((int) t.getX(), (int) t.getY()); figura.setRGB((int) (Math.random() * 256), (int) (Math.random() * 256), (int) (Math.random() * 256)); atualizaFigura(); } }); //com lambda this.setOnMouseDragged((t) -> { figura.setSegundoPonto((int) t.getX(), (int) t.getY()); atualizaFigura(); }); // this.setOnMouseReleased((t) -> { // figura.setSegundoPonto((int) t.getX(), (int) t.getY()); // atualizaFigura(); // }); } private void atualizaFigura() { elipse.setFill(Color.rgb(figura.getR(), figura.getG(), figura.getB())); elipse.setCenterX((figura.getXi() + figura.getXf()) / 2); elipse.setCenterY((figura.getYi() + figura.getYf()) / 2); elipse.setRadiusX(figura.getAltura() / 2); elipse.setRadiusY(figura.getLargura() / 2); } }package javafx03.logica; public class Figura { int xi, xf; int yi, yf; int r, g, b; public Figura() { } public int getXi() { return xi; } public int getXf() { return xf; } public int getYi() { return yi; } public int getYf() { return yf; } public int getR() { return r; } public int getG() { return g; } public int getB() { return b; } public void setRGB(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } public void setPrimeiroPonto(int x, int y) { this.xi = x; this.yi = y; } public void setSegundoPonto(int x, int y) { this.xf = x; this.yf = y; } public int getLargura() { return Math.abs(xf - xi); } public int getAltura() { return Math.abs(xf - xi); } }Tags : javafx, Programação avançada
JavaFX, exercício 1
package javafx02; import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; import javafx02.PaneOrganizer.PaneOrganizer; public class JavaFx02 extends Application { public static void main(String[] args) { launch(args); } @Override public void init() throws Exception { super.init(); System.out.println("onInit"); } @Override public void start(Stage stage) throws Exception { System.out.println("onStart"); PaneOrganizer brd = new PaneOrganizer(); Scene scene = new Scene(brd, 600, 400); stage.setScene(scene); stage.setTitle("Programação avançada"); stage.show(); } @Override public void stop() throws Exception { super.stop(); System.out.println("onStop"); } }
package javafx02.PaneOrganizer; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.BorderPane; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; public class PaneOrganizer extends BorderPane { Button btnVerde, btnAzul; Label lblVerde, lblAzul; int numerVerde, numeroAzul; public PaneOrganizer() { createPane(); //registerListener1(); //registerListener2(); //registerListener3(); //registerListener4(); registerListener5(); numerVerde=0; numeroAzul=0; } void createPane() { btnVerde = new Button("Green"); btnVerde.setTextFill(Color.GREEN); btnAzul = new Button("Blue"); btnAzul.setTextFill(Color.BLUE); //numeros dinamicos lblVerde = new Label("#Verde: 0"); lblAzul = new Label("#Azul: 0"); HBox hbox = new HBox(); hbox.getChildren().addAll(btnVerde, btnAzul); hbox.setSpacing(25); hbox.setPadding(new Insets(10)); hbox.setAlignment(Pos.CENTER); this.setTop(hbox); //numeros dinamicos HBox hboxlbl = new HBox(); hboxlbl.getChildren().addAll(lblVerde, lblAzul); hboxlbl.setSpacing(25); hboxlbl.setPadding(new Insets(10)); hboxlbl.setAlignment(Pos.CENTER); hboxlbl.setBackground(new Background(new BackgroundFill(Color.rgb(255, 255, 255), CornerRadii.EMPTY, Insets.EMPTY))); this.setBottom(hboxlbl); /* //outras coisas! Label l1 = new Label("Texto 1"); l1.setFont(Font.font("Times New Roman", FontWeight.BOLD, FontPosture.ITALIC, 24)); l1.setTextFill(Color.rgb(0, 255, 0)); l1.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY))); l1.setPadding(new Insets(10, 20, 30, 40)); //brd.setTop(l1); Label l2 = new Label("Texto 2"); l2.setTextFill(Color.rgb(0, 123, 123)); HBox hbox = new HBox(); hbox.getChildren().addAll(l1, l2); hbox.setStyle("-fx-background-color: #00ffff;"); //outra forma, css hbox.setPadding(new Insets(10)); hbox.setAlignment(Pos.CENTER); hbox.setSpacing(25); this.setTop(hbox); //brd.setBottom(l2); BorderPane.setAlignment(l2, Pos.CENTER); */ } //1ºalternativa, usando uma inner class void registerListener1() { btnVerde.setOnAction(new ProgBotaoVerde1()); //o que vai ser executado, um objecto do tipo! btnAzul.setOnAction(new ProgBotaoAzul1()); //o que vai ser executado, um objecto do tipo! } class ProgBotaoVerde1 implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent t) { //quando o botão for clicado System.out.println("Oi botão verde - inner class"); //aceder ao objecto de fora ou então criar uma varivel ou via construtor PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY))); } } class ProgBotaoAzul1 implements EventHandler<ActionEvent> { @Override public void handle(ActionEvent t) { //quando o botão for clicado System.out.println("Oi botão azul - inner class"); //aceder ao objecto de fora ou então criar uma varivel ou via construtor PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY))); } } //2ºalternativa, usando uma classe inline void registerListener2() { btnVerde.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { System.out.println("Oi botão azul - inline"); PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY))); } }); btnAzul.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { System.out.println("Oi botão azul - inline"); PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY))); } }); } //3ºalternativa, usando uma classe inline void registerListener3() { btnVerde.setOnAction(new ProcessaBotao(Color.GREEN)); //o que vai ser executado, um objecto do tipo! btnAzul.setOnAction(new ProcessaBotao(Color.BLUE)); //o que vai ser executado, um objecto do tipo! } class ProcessaBotao implements EventHandler<ActionEvent> { Color cor; public ProcessaBotao(Color cor) { this.cor = cor; } @Override public void handle(ActionEvent t) { //quando o botão for clicado System.out.println("Oi botão azul - inner class"); //aceder ao objecto de fora ou então criar uma varivel ou via construtor PaneOrganizer.this.setBackground(new Background(new BackgroundFill(cor, CornerRadii.EMPTY, Insets.EMPTY))); } } //4ºalternativa, usando lambda functions void registerListener4() { btnVerde.setOnAction( (t)->{ PaneOrganizer.this.setBackground( new Background(new BackgroundFill(Color.GREEN, CornerRadii.EMPTY, Insets.EMPTY))); }); btnAzul.setOnAction( (t)->{ PaneOrganizer.this.setBackground( new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY))); }); } //dinamicos void registerListener5() { btnVerde.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { System.out.println("Oi botão azul - inline"); PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 255, 0), CornerRadii.EMPTY, Insets.EMPTY))); numerVerde++; lblVerde.setText("#Verde: "+numerVerde); } }); btnAzul.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent t) { System.out.println("Oi botão azul - inline"); PaneOrganizer.this.setBackground(new Background(new BackgroundFill(Color.rgb(0, 0, 255), CornerRadii.EMPTY, Insets.EMPTY))); numeroAzul++; lblAzul.setText("#Azul: "+numeroAzul); } }); } }
JavaFX, construir aplicações com GUI em JAVA
Para começar a trabalhar com o JavaFX é necessário instalar na máquina:
https://openjfx.io/
que muito provavelmente vai redireccionar para o link:
https://gluonhq.com/products/javafx/
o download deve ser de:
openjfx-14.0.1_windows-x64_bin-sdk.zip
(ou então de uma versão LTS)
Em Windows depois é necessário descompactar o ZIP e copiar a pasta para:
\Program Files\Java
Antes de passar ao passo seguinte, recomendo que se instale a versão mais recente do Java, por exemplo a jdk-14.0.1_windows-x64_bin.exe e que desinstale todas as outras versões!!
Agora falta configurar o netbeans, v13:
criar uma nova biblioteca, tools->libraries
botão: new library
atribuir um nome, por exemplo JavaFX14
clicar em Add/JAR/Folder
e adicionar (seleccionar) todos os ficheiros jar da pasta: javafx-sdk-14.0.1\lib
voltar à janela principal do netbeans, e ir às propriedades do projeto
nas propriedades do projeto, escolher a opção libraries e clicar em classpath e libraries
escolher a opção JavaFX14
e no separador Run adicionar ao modulepath adicionar a biblioteca JavaFX14
ainda nas propriedades e do lado esquerdo, escolher Run, VM options:
–module-path /Program Files/Java/javafx-sdk-14.0.1/lib –add-modules javafx.controls
e done!
(ou seguir isto https://openjfx.io/openjfx-docs/#IDE-NetBeans)
package javafx01; import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; public class JavaFx01 extends Application { public static void main(String[] args) { //passamos os argumentos para o subsistema gráfico os parametros launch(args); } @Override //quando começa a aplicação public void start(Stage stage) throws Exception{ BorderPane bp = new BorderPane(); Label label1 = new Label("programação avançada"); Label label2 = new Label("outra label"); bp.setBottom(label1); bp.setTop(label2); //criar a cena Scene scene = new Scene(bp,400,400); //raiz da cena, e dimensões //adicionar a cena ao palco stage.setScene(scene); stage.show(); } }
Stage -> a janela da aplicação
Scene -> representa os elementos que vão estar presentes num stage
Node -> componentes específicos, são os nodes, que podemos ter dentro de cada cena (Canvas, ImageView, MediaView, Shape, Shape3D, SwingNode, EventTarget, Styleable: button, checkbox, combobox, label, listview, progressbar, radiobutton textfield)
Acerca dos objectos Pane existem estas opções:
Para projectos futuros:
em libraries->separador compile-> class path: e seleccionar a biblioteca JavaFX14
separador run -> modulepath: e seleccionar a biblioteca JavaFX14
e no run -> VM options -> –add-modules javafx.controls