JavaRush /Blogue Java /Random-PT /Introdução ao Java FX

Introdução ao Java FX

Publicado no grupo Random-PT
Um dia tive a ideia de escrever um pequeno aplicativo de desktop para minhas necessidades - algo como um pequeno dicionário para aprender palavras estrangeiras - e comecei a quebrar a cabeça, como poderia fazer isso? Naturalmente, a primeira coisa que me veio à mente foi Swing. Todo mundo provavelmente já ouviu falar de Swing . Esta é uma biblioteca para criar interfaces gráficas de usuário. Devido ao fato de nosso querido Oracle ainda não ter abandonado completamente o Swing, ele não é considerado obsoleto e os aplicativos ainda rodam nele. No entanto, ele não está mais sendo modernizado pelo Swing, e o pessoal da Oracle nos deu uma amostra do que será o futuro do JavaFX. E, de fato, JavaFX usa componentes Swing como provedor de serviços)

O que é JavaFX?

JavaFX é essencialmente um kit de ferramentas GUI para Java. Haverá uma pequena digressão aqui, e lembraremos o que é uma GUI : Interface gráfica do usuário - uma interface gráfica do usuário é um tipo de interface do usuário em que todos os elementos (botões, menus, ícones, listas) apresentados ao usuário no a exibição é feita na forma de imagens, gráficos. Ao contrário de uma interface de linha de comando, em uma GUI o usuário tem acesso aleatório a objetos visíveis usando dispositivos de entrada. Freqüentemente, os elementos da interface são implementados na forma de metáforas e exibem suas propriedades e finalidade para facilitar a compreensão do usuário. JavaFX tem como objetivo a criação de jogos e aplicativos de desktop em Java. Na verdade, ele substituirá o Swing devido à nova ferramenta GUI proposta para Java. Além disso, nos permite estilizar arquivos de layout GUI (XML) e torná-los mais elegantes usando CSS, semelhante ao que estamos acostumados em aplicações web. JavaFX também lida com gráficos 3D integrados, bem como áudio, vídeo e aplicativos de rede incorporados em um único kit de ferramentas GUI... É fácil de aprender e bem otimizado. Suporta muitos sistemas operacionais, bem como sistemas Windows, UNIX e Mac OS. Introdução ao Java FX - 3

Recursos JavaFX:

  • JavaFX inicialmente vem com um grande conjunto de peças de interface gráfica, como todos os tipos de botões, campos de texto, tabelas, árvores, menus, gráficos, etc., o que por sua vez nos poupará muito tempo.
  • JavaFX geralmente usa estilos CSS, e poderemos usar um formato FXML especial para criar a GUI, em vez de fazê-lo em código Java. Isso facilita o layout rápido de uma GUI ou a alteração da aparência ou composição sem a necessidade de brincar com o código Java por muito tempo.
  • JavaFX possui partes de diagrama prontas para uso, portanto não precisamos escrevê-las do zero sempre que você precisar de um diagrama básico.
  • JavaFX também vem com suporte a gráficos 3D, o que muitas vezes é útil se estivermos desenvolvendo algum tipo de jogo ou aplicativo semelhante.
Vamos dar uma olhada nos principais componentes da nossa janela:
  • O Stage é essencialmente uma janela circundante que atua como uma tela inicial e contém o restante dos componentes. Uma aplicação pode ter vários estágios, mas em qualquer caso deve haver um desses componentes. Essencialmente, o Stage é o contêiner principal e o ponto de entrada.
  • Cena - exibe o conteúdo do palco (como uma boneca aninhada). Cada estágio pode conter vários componentes - cena, que podem ser alternados entre si. Internamente, isso é implementado por um gráfico de objeto chamado Scene Graph (onde cada elemento é um nó, também chamado de Node ).
  • Nós são controles, como botões de rótulos ou até mesmo layouts, que podem ter vários componentes aninhados dentro deles. Cada cena pode ter um nó aninhado, mas pode ser um layout com vários componentes. O aninhamento pode ser de vários níveis, com layouts contendo outros layouts e componentes regulares. Cada nó tem seu próprio identificador, estilo, efeitos, estado e manipuladores de eventos.
Introdução ao Java FX - 4 Então, vamos avançar um pouco em direção ao código. Como uso o Java 8, não preciso puxar nenhuma dependência, já que o JavaFx está no JDK por padrão (como no Java 9.10), mas se tivermos o Java 11+, precisamos ir para o repositório maven e puxe-o de lá dependências.

JavaFX: exemplos de uso

Criamos uma classe regular com um método main(ponto de entrada):
public class AppFX extends Application {

    public static void main(String[] args) {
        Application.launch();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
pimarySatge.show();
    }
}
Aqui nossa classe herda javafx.application.Application(que temos da caixa Bugaga). Basicamente, chamamos o método estático Application launch()para abrir nossa janela. Além disso, nossa ideia reclamará que não implementamos o método Application, startque é o que fazemos em última análise. Para que serve? E para poder gerir as propriedades (a funcionalidade da nossa janela). Para fazer isso, usamos um argumento de entrada primaryStageno qual chamamos um método showpara que possamos ver a janela sendo aberta main. Vamos preencher um pouco nosso método start:
public void start(Stage primaryStage) throws Exception {
    primaryStage.setTitle("Dogs application");
    primaryStage.setWidth(500);
    primaryStage.setHeight(400);

    InputStream iconStream =
    getClass().getResourceAsStream("/images/someImage.png");
    Image image = new Image(iconStream);
    primaryStage.getIcons().add(image);

    Button button = new Button("WOF WOF ???'");

    button.setOnAction(e -< {
      Alert alert = new Alert(Alert.AlertType.INFORMATION, "WOF WOF WOF!!!");
        alert.showAndWait();
    });
    Scene primaryScene = new Scene(button);
    primaryStage.setScene(primaryScene);

    primaryStage.show();
}
Então, o que vemos aqui? Vamos analisar linha por linha: 2 - definir o nome da própria janela (estágio) 3.4 - definir suas dimensões 6.7 - definir o caminho do fluxo de leitura para o arquivo (ícone) Introdução ao Java FX - 58 - criar o arquivo como um objeto Imagem, que está conectado ao arquivo real pelo fluxo passado no construtor 9 - defina um ícone no painel superior da janela 11 - crie um objeto de botão 13-16 - defina a reação quando o botão for pressionado 17 - crie uma cena onde colocamos nosso botão 18 - colocamos a cena em nossa janela comum 20 - definimos a bandeira de visibilidade para a janela E como resultado obtemos uma pequena janela para receber nossos pesels favoritos: Introdução ao Java FX - 6Tudo parece muito mais simples que Swing, não é? Mas ainda não acabou. Não é bom escrever todo o código para exibir uma aplicação, é preciso dividi-lo de alguma forma para torná-lo mais compreensível (componentes gráficos em uma cesta, lógica em outra). E aqui o xml entra em cena... Ai meu Deus, xml? Exatamente. Especificamente, utilizamos sua implementação específica para JavaFX - FXML, na qual definimos os componentes gráficos da aplicação e suas propriedades (todos os tipos de tamanhos, etc.), e depois a conectamos a um controlador, que ajuda a gerenciar a lógica. Vejamos um exemplo deste xml:
<?xml version="1.0" encoding="UTF-8"?>
<?language javascript?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>

<VBox xmlns="http://javafx.com/javafx"  xmlns:fx="http://javafx.com/fxml"
   id="Dogs application" prefHeight="200" prefWidth="300" alignment="center">

  <Label text="Wow wow?"/>

  <Button fx:id="mainButton" text="Greeting" onAction="buttonClicked()"/>

    <fx:script>
       function buttonClicked() {
            mainButton.setText("Wow wow wow!!!")
       }
    </fx:script>
</VBox>
2 - a linguagem de script que usamos 4-6 - dados importados 8-9 Vbox - um contêiner que coloca subcomponentes em uma linha. 11 - exibe algum texto 13 - um botão, quando clicado, usamos o método descrito no script nas linhas 15 a 18. Deveria haver código para chamar esse arquivo xml no método start, mas agora isso não é tão importante, e nós irá omiti-lo (abaixo haverá um exemplo de extração deste arquivo). Então, xml é, claro, bom (mas não muito bom), escrevê-los manualmente é muito confuso, não estamos no século passado? Introdução ao Java FX - 7

Apresentando JavaFX SceneBuilder

É neste ponto que (rufar de tambores) entra em ação - SceneBuilder Em JavaFX Scene Builder é uma ferramenta com a qual podemos projetar nossas janelas em forma de interface gráfica e depois salvá-las, e este programa, com base no resultado, irá construir arquivos xml que iremos melhorar em nossa aplicação. A interface deste construtor fmxl é mais ou menos assim: Introdução ao Java FX - 8

Uma pequena digressão. Aulas de JavaFX

Vou pular os detalhes da instalação e também um estudo detalhado desta ferramenta. Esses são tópicos que vale a pena explorar mais. Portanto, ainda deixarei alguns links interessantes para aulas de JavaFX: um (tutorial online sobre JavaFX) e dois (outro bom tutorial). Vejamos um pequeno exemplo que esbocei. No final consegui algo como: Introdução ao Java FX - 9
(uma espécie de janela para registrar cães)
Quando você seleciona um cachorro e pressiona o botão Excluir, o cachorro é removido da nossa lista. Ao selecionar um amigo de quatro patas e alterar seus campos, e após pressionar o botão Editar, as informações do cachorro são atualizadas. Ao pressionar o botão Novo, aparece uma janela para criar um registro para um novo cão (para começar com seu nome): Introdução ao Java FX - 10Em seguida, clique em Salvar e preencha o restante de seus campos na primeira janela, e a seguir clique no botão Editar para salvar. Parece fácil, certo? Vamos ver como isso ficará em nosso aplicativo Java. Para começar, vou deixar aqui apenas os layouts xml dessas duas janelas geradas em SceneBuilder: Primeira (básica):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<AnchorPane prefHeight="300.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tutorial.controller.BaseController">
   <children>
      <SplitPane dividerPositions="0.29797979797979796" prefHeight="300.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
        <items>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
               <children>
                  <TableView fx:id="dogs" layoutX="-2.0" layoutY="-4.0" prefHeight="307.0" prefWidth="190.0" AnchorPane.bottomAnchor="-5.0" AnchorPane.leftAnchor="-2.0" AnchorPane.rightAnchor="-13.0" AnchorPane.topAnchor="-4.0">
                    <columns>
                      <TableColumn fx:id="nameList" prefWidth="100.33334350585938" text="Nickname" />
                    </columns>
                     <columnResizePolicy>
                        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
                     </columnResizePolicy>
                  </TableView>
               </children>
            </AnchorPane>
          <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
               <children>
                  <Label layoutX="49.0" layoutY="25.0" text="Person Details" AnchorPane.leftAnchor="5.0" AnchorPane.topAnchor="5.0" />
                  <GridPane accessibleText="erreererer" gridLinesVisible="true" layoutX="5.0" layoutY="31.0" AnchorPane.leftAnchor="5.0" AnchorPane.rightAnchor="5.0" AnchorPane.topAnchor="31.0">
                    <columnConstraints>
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                      <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
                    </columnConstraints>
                    <rowConstraints>
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                      <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                    </rowConstraints>
                     <children>
                        <Label prefHeight="17.0" prefWidth="70.0" text="Nickname" />
                        <Label text="Breed" GridPane.rowIndex="1" />
                        <Label text="Age" GridPane.rowIndex="2" />
                        <Label text="City" GridPane.rowIndex="3" />
                        <Label text="Level of training" GridPane.rowIndex="4" />
                        <TextField fx:id="breed" GridPane.columnIndex="1" GridPane.rowIndex="1" />
                        <TextField fx:id="age" GridPane.columnIndex="1" GridPane.rowIndex="2" />
                        <TextField fx:id="city" GridPane.columnIndex="1" GridPane.rowIndex="3" />
                        <TextField fx:id="levelOfTraining" GridPane.columnIndex="1" GridPane.rowIndex="4" />
                        <TextField fx:id="name" GridPane.columnIndex="1" />
                     </children>
                  </GridPane>
                  <Button layoutX="251.0" layoutY="259.0" mnemonicParsing="false" onAction="#create" text="New" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="230.0" AnchorPane.rightAnchor="130.0" AnchorPane.topAnchor="260.0" />
                  <Button layoutX="316.0" layoutY="262.0" mnemonicParsing="false" onAction="#edit" text="Edit" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="290.0" AnchorPane.rightAnchor="70.0" AnchorPane.topAnchor="260.0" />
                  <Button layoutX="360.0" layoutY="262.0" mnemonicParsing="false" onAction="#delete" text="Delete" AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="350.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="260.0" />
               </children>
            </AnchorPane>
        </items>
      </SplitPane>
   </children>
</AnchorPane>
Segundo (para criar novos cachorrinhos):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.text.Font?>
<AnchorPane prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.tutorial.controller.NewDogController">
   <children>
      <GridPane layoutX="31.0" layoutY="25.0" prefHeight="122.0" prefWidth="412.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="185.0" minWidth="10.0" prefWidth="149.0" />
          <ColumnConstraints hgrow="SOMETIMES" maxWidth="173.0" minWidth="10.0" prefWidth="146.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <Label prefHeight="48.0" prefWidth="178.0" text="Please, write name:">
               <font>
                  <Font size="20.0" />
               </font>
            </Label>
            <TextField fx:id="nickName" prefHeight="36.0" prefWidth="173.0" GridPane.columnIndex="1" />
         </children>
      </GridPane>
      <Button layoutX="222.0" layoutY="149.0" mnemonicParsing="false" onAction="#ok" prefHeight="37.0" prefWidth="95.0" text="Save" />
      <Button layoutX="325.0" layoutY="149.0" mnemonicParsing="false" onAction="#cansel" prefHeight="37.0" prefWidth="95.0" text="Cansel" />
   </children>
</AnchorPane>
Como é a estrutura de pastas: Introdução ao Java FX - 11Como você pode ver, não há nada de especial, existem controladores que representam determinadas janelas, existem modelos que representam nossos dados. Vamos dar uma olhada na classe que inicia o aplicativo (implementação do aplicativo): @Data
public class AppFX extends Application {

    private Stage primaryStage;
    private AnchorPane rootLayout;
    private ObservableList listDog = FXCollections.observableArrayList();

    public AppFX() {
        listDog.add(new Dog("Fluffy", "Pug", 8, "Odessa", 2));
        listDog.add(new Dog("Archie", "Poodle", 3, "Lviv", 6));
        listDog.add(new Dog("Willie", "Bulldog", 5, "Kiev", 4));
        listDog.add(new Dog("Hector", "Shepherd", 9, "Minsk", 6));
        listDog.add(new Dog("Duncan", "Dachshund", 1, "Hogwarts", 9));
    }
Aqui vemos um construtor que irá preencher nossos dados iniciais (que armazenamos em uma planilha especial - ObservableList).
public static void main(String[] args) {
        Application.launch();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        this.primaryStage = primaryStage;
        this.primaryStage.setTitle("Dogs application");
        showBaseWindow();
}
Nada de especial - maine a implementação start()que inicia o aplicativo:
public void showBaseWindow() {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(AppFX.class.getResource("/maket/rootWindow.fxml"));
            rootLayout = loader.load();
            Scene scene = new Scene(rootLayout);
            primaryStage.setScene(scene);
            InputStream iconStream = getClass().getResourceAsStream("/icons/someImage.png");
            Image image = new Image(iconStream);
            primaryStage.getIcons().add(image);
            BaseController controller = loader.getController();
            controller.setAppFX(this);
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
Então, aqui vemos o método que realmente iniciamos start(), ou seja, aquele que define as configurações da nossa janela base. Como no layout xml em recursos: dando a ele um ícone, vinculando-o a um controlador específico e dando ao controlador um link para thisa classe)
public void showCreateWindow(Dog dog) {
        try {
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(AppFX.class.getResource("/maket/new.fxml"));
            AnchorPane page = loader.load();
            Stage dialogStage = new Stage();
            dialogStage.setTitle("Wow Wow Wow");
            dialogStage.initModality(Modality.WINDOW_MODAL);
            dialogStage.initOwner(primaryStage);
            dialogStage.setScene(new Scene(page));
            CreateController controller = loader.getController();
            controller.setDialogStage(dialogStage);
            controller.setDog(dog);
            dialogStage.showAndWait();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Aqui vemos o método responsável pelo aparecimento da segunda janela - a janela de criação de um novo registro (o nome de um novo cachorro). Também definimos o controlador, layout xml, palco, etc... A próxima classe que consideraremos será um modelo representando nosso cachorro (informações sobre ele): @Data
public class Dog {

    private StringProperty name;
    private StringProperty breed;
    private IntegerProperty age;
    private StringProperty city;
    private IntegerProperty levelOfTraining;

    public Dog(String name, String breed, int age, String city, int levelOfTraining) {
        this.name = new SimpleStringProperty(name);
        this.breed = new SimpleStringProperty(breed);
        this.age = new SimpleIntegerProperty(age);
        this.city = new SimpleStringProperty(city);
        this.levelOfTraining = new SimpleIntegerProperty(levelOfTraining);
    }

    public Dog() {
        name = new SimpleStringProperty();
        breed = null;
        age = null;
        city = null;
        levelOfTraining = null;
    }
}
Aqui vemos dois construtores. Um é quase regular com todos os argumentos (quase, porque usamos wrappers FX especiais de tipos simples) e um construtor sem argumentos: usamos ao criar um novo cachorro, que a princípio tem apenas um nome. Controlador para janela básica: @Data
public class BaseController {

    @FXML
    private TableView dogs;
    @FXML
    private TableColumn nameList;
    @FXML
    private TextField name;
    @FXML
    private TextField breed;
    @FXML
    private TextField age;
    @FXML
    private TextField city;
    @FXML
    private TextField levelOfTraining;
    private AppFX appFX;
Aqui vemos nossos campos de objeto, mas no formato TextField. Este é um formato que representa um campo de entrada de texto. @FXML é uma anotação projetada para vincular o código Java e o objeto correspondente do nosso layout (botão, campo ou qualquer outro).
@FXML
private void initialize() {
    nameList.setCellValueFactory(
            cellData -> cellData.getValue().getName());
    dogs.getSelectionModel().selectedItemProperty().addListener(
            (observable, oldValue, newValue) -> showDogsInformation(newValue));
}
Aqui vemos um método para exibir nomes de cães, à direita da lista (sua anotação @FXML se liga ao componente de layout JavaFX TableView).
public void setAppFX(AppFX appFX) {
    this.appFX = appFX;
    dogs.setItems(appFX.getListDog());
}

private void showDogsInformation(Dog dog) {
    if (dog != null) {
        name.setText(dog.getName() != null ? dog.getName().getValue() : null);
        breed.setText(dog.getBreed() != null ? dog.getBreed().getValue() : null);
        age.setText(dog.getAge() != null ? String.valueOf(dog.getAge().get()) : null);
        city.setText(dog.getCity() != null ? dog.getCity().getValue() : null);
        levelOfTraining.setText(dog.getLevelOfTraining() != null ? String.valueOf(dog.getLevelOfTraining().get()) : null);
    } else {
        name.setText("");
        breed.setText("");
        age.setText("");
        city.setText("");
        levelOfTraining.setText("");
    }
}
No primeiro método vemos definir uma referência interna para a classe que implementa Application (para que possamos chamar seu método para chamar a segunda janela), e definir a lista inicial a ser exibida. O segundo verifica se o cão atual possui determinados dados e, com base nisso, define os campos de texto:
@FXML
    private void delete() {
        int selectedIndex = dogs.getSelectionModel().getSelectedIndex();
        dogs.getItems().remove(selectedIndex);
    }

    @FXML
    private void edit() {
        int selectedIndex = dogs.getSelectionModel().getSelectedIndex();
        dogs.getItems().set(selectedIndex, new Dog(name.getText(), breed.getText(), Integer.valueOf(age.getText()), city.getText(), Integer.valueOf(levelOfTraining.getText())));
    }

    @FXML
    private void create() {
        Dog someDog = new Dog();
        appFX.showCreateWindow(someDog);
        if (someDog.getName() != null && !someDog.getName().getValue().isEmpty()) {
            appFX.getListDog().add(someDog);
        }
    }
}
Aqui vemos três métodos da janela base associados aos botões: Introdução ao Java FX - 12
  • delete — exclui o cachorro selecionado (selecionado) por índice;
  • editar - crie um novo cachorro com os dados transferidos e instale-o no lugar do anterior;
  • criar - criamos um novo cachorro e chamamos o método de chamar a janela de criação, passando um novo objeto, e após fechá-lo, se o nome não for nulo, salvamos o novo animal de estimação.
Seguindo em frente, o controlador de janela para criação do cachorro: @Data
public class CreateController {
    private Stage dialogStage;
    private Dog dog;

    @FXML
    private TextField nickName;

    @FXML
    private void ok() {
        if (nickName != null && !nickName.getText().isEmpty()) {
            dog.setName(new SimpleStringProperty(nickName.getText()));
            dialogStage.close();
        }
    }

    @FXML
    private void cansel() {
        dialogStage.close();
    }
}
Aqui vemos a conexão com o campo de texto da janela, processando os botões Salvar e Cancelar, que de alguma forma fecham a janela. Como você pode ver, para maior comodidade na minha pequena aplicação, usei o Lombok, caso contrário o código teria crescido muito e eu não o teria encaixado na minha análise. Provavelmente é tudo o que tenho por hoje. Hoje conhecemos brevemente os conceitos básicos e um exemplo de uso de JavaFX, e podemos construir pequenas aplicações desktop (usando informações adicionais, que, felizmente, abundam na Internet). E você, por sua vez, gosta))
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION