Como fazer com que a mudança de dados reflita na aplicação?
Imagine que sua aplicação você tem um campo de entrada texto, um TextField, e em seguida um texto, um Label, para mostrar o que tem nesse campo, em tempo real, como você faria?Bem, a forma mais usual é adicionar um key handler ao campo de texto e quando esse for acionado, configurar o texto do Label. Veja o código:
TextField txtNome = new TextField(); Label lblNome = new Label(); txtNome.setOnKeyReleased(e -> { lblNome.setText(txtNome.getText()); }); lblNome.setFont(Font.font("Verdana", FontWeight.BOLD, 30));
Esse cenário, no entanto, pode ser muito mais complexo e levar você a ter que espalhar listeners na aplicação para fazer com que duas propriedades reflitam uma a outra e, o pior de tudo, o código pode não ficar tão legível e de difícil manutenção.
Properties
Felizmente o JavaFX tem uma API de properties, onde foram criadas diversas classes que adicionam "superpoderes" aos tipos mais comuns do java e que podem ser extensíveis para usarmos com nosso objetos da aplicação.
O melhor ainda é que toda a API do JavaFX está preparada para isso. Todos os campos de um elemento do JavaFX, como o Node, têm um equivalente com property. Por exemplo, um campo de texto tem um textProperty para o valor de texto do mesmo.
Binding
Um super poder muito notável das properties é o binding. Realizar binding significa fazer com que uma propriedade tenha seu valor modificado em função da outra. Exemplificando, se temos o texto A e queremos que o texto B seja o mesmo que o A, mas mais ainda, que B atualize quando B atualizar, dizemos que B está amarrado à A (bound). Quando isso acontece, B não pode ser modificado diretamente ou teremos um erro. Se quiser mudar B, teremos que mudar A. Isso em código fica como mostrado a seguir (reescrevendo nosso exemplo anterior):TextField txtNome = new TextField(); Label lblNome = new Label(); lblNome.textProperty().bind(txtNome.textProperty()); lblNome.setFont(Font.font("Verdana", FontWeight.BOLD, 30));
Simples, não? Aqui pode não parecer tão intessante, mas imagine que estamos em uma aplicação que traz objetos do banco de dados e queremos refletir os dados desses objetos na view em tempo real. Não seria essa uma boa alternativa? Adicionalmente podemos realizar um binding bidirecional, onde se au altero A, B também será alterado, e se eu altero B, A também terá o conteúdo de B!
Note que por enquanto falamos somente de texto, mas isso é possível com valores boleanos, inteiros e do tipo double:
StringProperty: Para textos (textProperty no TextField ou no Label)
BooleanProperty: Valores booleanos( visibleProperty, disableProperty no Node)
DoubleProperty: Valores double (value no Slider)
IntegerProperty: Valores inteiros (int)
ObjectProperty: Objetos no geral.
Cada uma das properties acima tem um equivalente para ser instanciado caso você queria usar as properties em sua aplicação, por exemplo: SimpleStringProperty, SimpleBooleanProperty, etc..
Agora, para introduzir a API, vamos mostrar brevemente tudo isso. Futuramente voltamos a aprofundar nesse tópico.
Binding com expressões
Imagine agora que você queira não só amarrar um texto, mas amarrar um texto com outro e ainda um texto fixo no meio, como fazer? Ou, se estiver usando números, quer que um valor seja sempre o resultado da soma desses dois números. Isso é muitp fácil e é possível através das expressões das properties.Por exemplo, para String, temos a expressão concat, onde passamos passar outra String literal ou outra String property e cancat irá retornar uma StringProperty que é atualizada com o resultado daquela concatenação, veja um simples exemplo:
TextField txtNome = new TextField(); TextField txtSaudacao = new TextField(); Label lblNome = new Label(); lblNome.textProperty().bind( txtNome.textProperty().concat(", ") .concat(txtSaudacao.textProperty() ));
Claro que nem sempre queremos que o resultado dessa expressão seja outro texto, logo, temos também expressões da String que retornam uma propriedade de outro tipo. Por exemplo, a expressão greaterThanOrEqualTo irá retornar uma BooleanProperty de comparação de duas StringProperty. Um outro exemplo é gerar um resultando de soma de duas DoubleProperty e uma String que contenha a representação em texto, veja:
Slider sld1 = new Slider(0, 100, 50); Slider sld2 = new Slider(0, 100, 50); Label lblResultado = new Label(); lblResultado.textProperty().bind( sld1.valueProperty() .add(sld2.valueProperty()) .asString() );
Bem, paramos por aqui, mas a javadoc é sua amiga. Leia por lá e navegue na API que em breve você está mestre nisso!
Observando mudanças
Lembra do padrão observer? Um super poder das properties é também deixar que você observe ela. Assim, quando ela muda temos um pedaço de código notificado. Esse código notificado irá receber três parâmetros: o valor observável que sofreu a mudança, o valor que era antes, o novo valor. Simples, não? Com Java 8 é ainda mais simples pois podemos usar Lambdas para definir os ChangeListeners. Veja o seguinte exemplo que observamos as mudanças de um combo box:
String valores[] = { "Garrafa", "Copo", "Monitor", "Notebook", "Celular" }; // na próxima falamos de FXCollections ComboBoxcmbValores = new ComboBox ( FXCollections.observableArrayList(valores)); Label lblAntigo = new Label(); Label lblNovo = new Label(); cmbValores.valueProperty().addListener((obs, velho, novo) -> { lblAntigo.setText("Valor Antigo: " + velho); lblNovo.setText("Valor Novo: " + novo); });
Muito bom o artigo, estou desenvolvendo um software com JavaFX e foi de grande valia este post, procurei por muito na web e até que enfim consegui uma explanação sobre o assunto.
ResponderExcluirParabéns!
Parabéns pelo ótimo post! Como falou o amigo acima, o conteúdo ajuda muito no desenvolvimento de aplicações jfx.
ResponderExcluirValeu!