Mostrando postagens com marcador controles. Mostrar todas as postagens
Mostrando postagens com marcador controles. Mostrar todas as postagens

28 de fev. de 2016

Criando controles customizados com JavaFX

Essa é uma tradução do artigo criado por Hendrik. Veja mais no blog GuiGarage

Por Hendrik Ebbers


Uma coisa que eu frequentemente faço com Java Swing é a customização de componentes e a criação de novos tipos de componentes. Um exemplo é o JGrid. Desde que JavaFX foi lançado, eu quis portar o JGrid. Depois de muitos experimentos e protótipos ruins, eu acho que encontrei a forma correta de fazer isso. As apresentações de Gerrit Grunwald e Jonathan Giles no JavaOne me ajudaram muito a fazer isso. As gravações dessas apresentações estão disponívels online (link e link - NOTA: links quebrados), então eu recomendo quem estiver interessado nesse tópico a investir algum tempo e assistir elas.


Primeiros passos



Como componente de interface no JavaFX é composto por um control(controle), uma skin("casca") e um behaviour(comportamento). Em um caso ideal, há também a parte do CSS.


A melhor forma de começar é criar uma classe que herda de  javafx.scene.control.Control. Essa classe é basicamente compara ao JComponent. O Control deve ter as propriedades do componente e atuar como a classe principal dele porque instâncias dessa classe serão depois criadas automaticamente no códido da aplicação e serão adicionadas para a árvore da interface com o usuário(UI):

MyCustomControl myControl = new MyCustomControl();
panel.getChildren().add(myControl);

Quando programando componentes Swing na forma correta você coloca tudo que depende da visualização ou a interação com o usuário em uma classe de UI (veja LabelUI por exemplo). JavaFX vai além e disponibiliza uma classe de skin para toda a visualização e o layout relacionado com o código e a classe behaviour para todas as interações com o usuário.



Para fazer isso em JavaFX você deve entender como as classes se relacionam. Aqui está um pequeno diagrama que mostra as relações entre essas classes:



Criando o comportamento (classe Behaviour)

Se o seu componente é somente para visualização de dados e não há interação, é baste simples criar um comportamento. Assim, você simplesmente precisa criar um classe que herde de com.sun.javafx.scene.control.behavior.BehaviorBase.

public class MyCustomControlBehavior extends BehaviorBase {
public MyCustomControlSkin(MyCustomControl control) {
super(control, new MyCustomControlBehavior(control));
    }
}

Alguns de vocês podem ficar confusos quando ver o pacote da classe BehavioyBase. No momento é uma API privada e normalmente você não deveria usar essas classes no seu código, mas os caras da Oracle sabem desse problema e irão providencias a classe BehaviorBase como uma API com o JavaFX 8. Então uma boa prática é usar essa classe privada agora e refatorar assim que o JavaFX for lançado(*).

NOTA: O javaFX 8 foi lançado já!

Criando a casca (classe Skin)

Depois que a classe de comportamento é criada, podemos dar uma olhada na skin. Sua classe de skin vai muito provavelmente herdar de com.sun.javafx.scene.control.skin.BaseSkin e criar um novo comportamento para o seu Control. O código normalmente se parece com o seguinte:

public class MyCustomControlSkin extends SkinBase{
public MyCustomControlSkin(MyCustomControl control) {
super(control, new MyCustomControlBehavior(control)); }
}

Criando o controle

A última classe que precisamos é o controle. Primeiro criamos uma classe vazia:

public class MyCustomControl extends Control {
public MyCustomControl() { }
}

Nesse ponto nós temos uma falha nas dependências das nossas classes. A skin conhece o behavior e o control. Aqui tudo parece correto, no entanto, no código da aplicação você vaim simplesmente criar um novo controle e usar como eu mostrei antes. O problema é que a classe de controle não sabe nada sobre a skin ou o behavior. Esse foi um dos maiores desafios quando eu estava aprendendo JavaFX.

Juntando as coisas


O que inicialmente parece um grande problema, é na verdade parte do poder disponibilizado pelo JavaFX. Com JavaFX é muito fácil criar diferente visualizações (skins) para os controles. Nessa parte você pode customizar a aparência dos componentes usando CSS. Como a skin é a parte principal da aparência, ela deve ser definida no CSS também. Assim, invés criar um objeto skin para o controle manualmente, você simplesmente define a classe skin que deve ser usada no seu controle. A criação e tudo mais é automaticamente feito pelas APIs do JavaFX. Para fazer isso, você deve ligar o seu controle a uma classe CSS.
Primeiramente, crie um novo arquivo no seu projeto. Eu acredito que a melhor prática é usar o mesmo pacote que o controle está e não um arquivo CSS criado sob src/main/resource:


Dentro do seu CSS você deve especificar um novo seletor para o seu componente e adicionar a skin como uma propriedade. Isso deveria parecer como o seguinte exemplo:

.custom-control {
-fx-skin: "com.guigarage.customcontrol.MyCustomControlSkin";
}

Assim que você criar o CSS, você tem que definir o mesmo no seu controle. Portanto, você deve configurar o caminho para o arquivo css e o seletor dos seus componentes:

public class MyCustomControl extends Control {
public MyCustomControl() {
getStyleClass().add("custom-control");
}
protected String getUserAgentStylesheet() {
return MyCustomControl.class.getResource("customcontrol.css").toExternalForm();
}
}

Depois que todas essas coisas foram feitas corretamente, JavaFX vai criar uma instância de skin para o seu controle. Você não precisa se preocupar com instanciação ou o mecanismo de depdendëncias. Nesse ponto eu gostaria de agradecer Jonathan Giles, que dedicou um tempo para codificar a integração do css para o gridfx  comigo e explicou todo o mecanismo e benefícios.

Acesso à Skin e Behavior

Normalmente não há a necessidade de acessar a Skin e o Behavior através do controle, mas se você precisar disso, você pode acessá-los da seguinte forma:


Como controler.getSkin() rece um javafx.scene.control.Skin e não a classe SkinBase você deve fazer um cast se precisar do behaviour:


((SkinBase)getSkin()).getBehavior();

Uma solução para os que odeiam CSS

Para alguns de vocës esse mecanimos parece um pouco exagerado. Talvez vocë simplesmente precise de um controle na sua aplicação e você não planeja usar uma skin com CSS e fazer tudo isso. Para esse caso de uso, há um ótimo workaround na API do JavaFX. Você pode ignorar toda a parte do CSS e configurar a classe skin no seu controle usando código:

public class MyCustomControl extends Control {
public MyCustomControl() {
setSkinClassName(MyCustomControlSkin.class.getName());
}
}


O benefício dessa forma de trabalho é que refatorar pacotes e nome de classes não vai quebrar o seu código e você não precisa de um arquivo CSS extra. Por outro lado, há uma grande desvantagem. Você não poderia usar skins definidas por CSS em qualquer extensão do seu controle. Acho que toda API pública, como o GridFX, deveria usar a forma com CSS. Em alguns casos de uso internos, a forma hardcoded(essa que falamos agora) é muito mais rápida.

Conclusão

Agora nós criamos um controle, uma skin e um behavior que está funcionando bem e pode ser adicionado na árvore  UI da sua aplicação. Mas, como acontece com o Swing, simplesmente herdar de JComponent não levará você a ver nada na tela. Então o próximo passo é adicionar estilo e organizar o layout do seu componente. Irei falar disso no meu próximo artigo.
Se você quiser dar uma olhada em algum componente existente veja  jgridfx ou JFXtras. No jgridfx os seguintes arquivos batem com esse artigo:

  • com.guigarage.fx.grid.GridView (control)
  • com.guigarage.fx.grid.skin.GridViewSkin (skin)
  • com.guigarage.fx.grid.behavior.GridViewBehavior (behavior)
  • /src/main/resources/com/guigarage/fx/grid/gridview.css (css)





1 de ago. de 2014

Organizando conteúdo em paineis com o TabPane

Anteriormente no nosso blog, apresentamos diversas formas de gerenciamento de layout:


Uma forma básica de se dispor conteúdo, no entanto, está em disponibilizar eles em paineis. Hoje vamos apresentar como apresentar conteúdos usando o TabPane.

TabPane

Para organizarmos  os componentes em paineis acessados através de abas, podemos utilizar a classe TabPane, onde podemos adicionar paineis e o JavaFX automaticamente irá organizar eles em abas, veja:


Criando e customizando o TabPane

Criar um painel de Abas não é nada difícil. Você deve saber que a classe TabPane herda de nó e tem um atributo getTabs que retorna uma lista de abas, onde você pode adicionar instâncias de Tab, que representam uma Aba. Cada Aba tem como principais atributos o texto, um gráfico que representa um ícone e se a aba é "fechável" ou não. Veja abaixo como fica o código para criar o painel que mostramos acima:

// criamos o painel
TabPane painelAbas = new TabPane();
// criando paineis de abas, o construtor recebe o nome da aba
Tab aba1 = new Tab("Aba 1");
Tab aba2 = new Tab("Aba 2");
Tab aba3 = new Tab("Aba 3");
//configuramos o conteúdo de cada aba, que aceita qualquer tipo de Nó
aba1.setContent(new Label("Configurando como conteúdo um label..."));
aba2.setContent(new Button("Um botão de conteúdo"));
aba3.setContent(new Rectangle(150, 50));
// abas podem ser fechadas, ou não. Por padrão elas são.
aba1.setClosable(false);
aba2.setClosable(false);
aba3.setClosable(true);
// podemos adicionar ícones as abas
aba1.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("coracao.png"))));
aba2.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("fogo.png"))));
aba3.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("agua.png"))));
// agora adicionamos todas as abas de vez
painelAbas.getTabs().addAll(aba1, aba2, aba3);

Para o a instância do TabPane, temos um atributo que permite escolher o lado que a Aba pode ficar: em baixo, à esquerda, à direita ou acima (padrão). Isso é possível através do método setSide, que aceita um dos valores do Enum javafx.geometry.Side. Por exemplo, a chamada do método setSide(Side.BOTTOM) gera o seguinte:



Conclusão

Apresentamos o painel de Abas do JavaFX! O nosso próximo objetivo é o Accordion e aí fecharemos os principais controles. Acesse o código completo no github

Estamos próximos de cobrir todos os aspectos básicos do JavaFX! Os tópicos desse blog serão organizados em um mini ebook para principiantes na tecnologia e será disponibilizado gratuitamente!

31 de jul. de 2014

Estruturas de Árvore com TreeView

Um dos maiores usos de aplicações gráficas é para criação de aplicações onde o usuário interage através de controles. Já abordamos muitos controles aqui no blog:


Hoje iremos apresentar os controle TreeView!

TreeView

TreeView é classe que usamos para visualizar dados em formatos de árvore ou com uma estrutura hierárquica(como uma estrutura de diretórios). Formado por células, podemos usar uma estrutura hierarquica onde uma célula pode ter outras células filhas. Veja:

A classe do TreeView está no pacote javafx.scene.control.

Usando o TreeView

A classe TreeView, como muitos espertinhos já devem ter adivinhado, herda de Node, ou seja, podemos aplicar efeitos, rotacionar, etc... Veja uma a mesma árvore mostrada acima rotacionada e com um efeito de "Blur"

O elemento principal que se relaciona com o TreeView é o TreeItem, que representa os itens da árvore.  Agora note algo interessante: um Item pode ter como filhinho vários outros items, assim criamos a estrutura de árvore. Ou seja, temos um item raiz e ele pode ter filhinhos, e os filhinhos, mais filhinhos, e assim vai... Na TreeView, só informamos o nó raiz. Enfim, veja o código que gerou a árvore acima:

// a nossa árvore
TreeView arvore = new TreeView<>();
// Esse é o item raiz da nossa árvore. Embaixo dele temos que colocar
// mais itens
TreeItem raiz = new TreeItem("Raiz");
// Os itens podem ser aninhados, abaixo criamos dois items filhos no
// nível 1 e para cada um desses, três filhotes no nível 2
for (int i = 0; i < 2; i++) {
 TreeItem lvl1 = new TreeItem("LVL1 " + i);
 for (int j = 0; j < 3; j++) {
  lvl1.getChildren().add(new TreeItem("LVL2 " + j));
 }
 raiz.getChildren().add(lvl1);
}
// você pode expandir para por padrão mostrar os filhos de um item.
// Vamos fazer isso com a nossa raiz
raiz.setExpanded(true);
// agora setamos a raiz da nossa árvore
arvore.setRoot(raiz);

Conclusão

Apresentamos mais um controle do JavaFX. O código pode ser visto no nosso github e usamos Java 8!

15 de ago. de 2012

Controles Básicos de Interface III


Na terceira parte de nossa série sobre os controles do JavaFX, vamos falar das caixas de combinação, ComboBox, e caixas de escolhas, ChoiceBox. Ambas permitem você escolher um valor entre um conjunto de opções. Além do que mostrar como usar o controle, vamos ainda explorar os eventos que ele oferece,criando uma aplicação um pouco mais interessante do que algo inútil

Muitas opções, uma possível escolha

Os dois controles que vamos apresentar hoje funcionam de maneira muito semelhante, a maior diferença está na forma visual que elas são apresentadas dentro da sua aplicação. Ambas permitem que você faça a escolha de uma só opção entre diversas outras. no entanto, há uma pequena diferença visual entre elas e o ComboBox permite que você fabrique as células que serão mostradas. Criar fábrica de células é um assunto que facilmente ocuparia uma postagem inteira, você pode ver mais sobre isso aqui.
Para tratar dos dados individualmente da parte visual, usamos em ambas classes o conceito de SelectionModel, onde abstraimos a parte de seleção de dados. Temos duas abstrações: MultipleSelectionModel(permite selecionar vários itens ao mesmo tempo) e SingleSelectionModel(permite selecionar um único item ao mesmo tempo.
Hoje também vamos falar de um conceito novo e específico do JavaFX, então vamos nos limitar a mostrar o ComboBox e uma aplicação super simples, no entanto, o leitor deve pode acessar a documentação do ChoiceBox e criar a mesma aplicação usando ele. Enfim, vamos ao código!

Selecione a cor

Para demostrar o ComboBox, criamos uma aplicação bem simples que permite que você selecione a cor da cena e de um retângulo através do uso desse controle de interface. Ao selecionar a cor, a mudança já é imediata. Como fazemos isso? Confira o código e em seguida tenha acesso

package main;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class Main extends Application {

 String cores[] = { "Blue", "Black", "Red", "White", "Green", "Yellow",
   "Gray", "Pink", "Salmon" };

 public static void main(String[] args) {

  launch();
 }

 @Override
 public void start(Stage palco) throws Exception {
  StackPane raiz = new StackPane();

  HBox opcoes = new HBox(10);
  opcoes.setAlignment(Pos.CENTER);

  final Rectangle retangulo = new Rectangle(300, 100);

  ComboBox cbCorCena = new ComboBox<>(); // 1
  ComboBox cbCorRetangulo = new ComboBox<>();

  cbCorCena.getItems().addAll(cores); // 2
  cbCorRetangulo.getItems().addAll(cores);

  opcoes.getChildren().addAll(cbCorCena, cbCorRetangulo);

  raiz.getChildren().addAll(retangulo, opcoes);

  final Scene cena = new Scene(raiz, 450, 250);
  palco.setTitle("Uso de ComboBox");
  palco.setScene(cena);
  palco.show();
  // 3
  cbCorCena.getSelectionModel().selectedItemProperty()
    .addListener(new ChangeListener() {
     @Override
     public void changed(
       ObservableValue valorObservado,
       String valorAntigo, String valorNovo) {
      cena.setFill(Color.valueOf(valorNovo));
     }

    });
  cbCorRetangulo.getSelectionModel().selectedItemProperty()
    .addListener(new ChangeListener() {
     @Override
     public void changed(
       ObservableValue valorObservado,
       String valorAntigo, String valorNovo) {
      // 4
      retangulo.setFill(Color.valueOf(valorNovo));
     }

    });
  cbCorCena.getSelectionModel().select(0); // 5
  cbCorRetangulo.getSelectionModel().select(1);
 }
}


  1. Começamos instanciando uma ComboBox. Perceba que ela faz uso de genéricos, onde informamos que tipo de dado serão os itens contidos nela, no nossa caso, String;
  2. Nessa linha adicionamos os itens. Isso é feito de forma semelhante com a qual adicionamos itens a um componente pai como o Grupo, VBox, HBox, entre outros. Nesse caso estamos adicionando uma lista de Strings, pois esse é o tipo aceitado por essa combobox;
  3. Para observar quando o usuário escolhe algum valor no Combox, adicionamos um ouvinte ao valor observável a propriedade do item selecionado. Isso pode parecer bastante estranho, mas em breve você vai se acostumar. JavaFX traz uma API com atributos diferentes, onde nós podemos observar as modificações feitas nesses atributos e tomar alguma ação. Novamente isso é feito através de Listeners, no nosso caso usamos um listener de mudança, o ChangeListener. Note que o uso dele é bastante semelhante ao uso dos listeners de botões. Em resumo, nós estamos observando o valor selecionado do combox através de um listener, quando ele muda, nós trocamos a cor de fundo da cena;
  4. A classe Color do JavaFX é bastante interessante e contém muito mais do que um aglomerado de constantes para definir cores. É possível também converter o valor de texto de uma cor para um objeto Color. É isso o que fazermos aqui;
  5. Por fim nós usamos o método select do modelo de seleção para escolher uma cor. Esse método equivale a ação de um usuário ao escolher uma cor na combobox usando a sua aplicação, ou seja, isso também vai disparar o ouvinte de mudança que falamos na explicação 3.
Por fim, essa é a aplicação resultante:


Conclusão

Hoje nós mostramos o combobox e também apresentamos um conceito muito importante do JavaFX que são as "Properties". Você já deve ter percebido que há muito coisa a ser esclarecida e faremos isso aos poucos. 
Já está chegando o dia da nossa primeira aplicação do "mundo real", vamos apresentar mais alguns conceitos e em breve poderemos começar a criar coisas mais interessantes com JavaFX.