Swing Application Framework (JSR-296) - Parte 2

Este blog deixou de ser mantido, mas o autor continua escrevendo aqui. Não deixe de assinar o novo feed!

Na primeira parte desse artigo, foram apresentadas as motivações e os objetivos para criação e uso da JSR-296, o gerenciamento de ciclo de vida e inicialização de aplicativos, o armazenamento do estado da GUI entre sessões e persistência local com o Swing Application Framework. Nessa continuação, será apresentada a injeção de recursos e a definição, gerenciamento e binding de Action’s.

Injeção de Recursos

A classe application.ResourceMap, segundo sua documentação, pode ser utilizada para “injetar” recursos nas propriedades dos componentes Swing e em campos de objetos. O método injectComponents [dessa classe] usa a propriedade name de java.awt.Component para relacionar o nome de recursos com propriedades. O método injectFields [de ResourceMap] "injeta" valores dos recursos nos campos anotados com @Resource.

Ou seja, é através da propriedade name que é feito o binding entre uma certa propriedade do objeto e o recurso a ser injetado nessa propriedade. Os recursos são definidos em arquivos de propriedades. ResourceMap é acessada através do método getResourceMap de ApplicationContext.

Por exemplo, no método startup abaixo há injeção de uma string (o recurso) na propriedade title de JFrame.

  1.  
  2. protected void startup() {
  3.     final ApplicationContext ac = ApplicationContext.getInstance();
  4.  
  5.     final ResourceMap resourceMap = ac.getResourceMap(getClass());
  6.     resourceMap.injectComponents(mainFrame);
  7. }
  8.  

O método startup faz parte da subclasse concreta de Application, denominada OhMyApplication (veja a parte 1 desse artigo), presente no pacote gui. Nesse exemplo, o método injectComponents injeta recursos definidos no arquivo de propriedades OhMyApplication.properties, presente no pacote gui.resources:

  1.  
  2. Application.name = OhMy
  3. Application.id = OhMy-2.0
  4. Application.title = OhMy-2.0
  5. Application.version = 2.0
  6. Application.vendor = Rafael Fiume
  7. Application.vendorId = RFiume
  8. Application.homepage = http://rfiume.blogspot.com/
  9. Application.description = Pequeno sistema de informação gerencial.
  10. Application.lookAndFeel = com.jgoodies.looks.plastic.PlasticXPLookAndFeel
  11.  
  12. # Essa expressão será injetada na propriedade title de JFrame
  13. mainFrame.title = ${Application.name}-${Application.version}
  14.  

mainFrame é o valor da propriedade name (i.e. obtido através de getName) do objeto mainFrame, e title é o nome da propriedade de JFrame na qual queremos injetar o recurso - nesse caso, uma string com o título da janela.

Injeção de Recursos com Fuse

Com a injeção de recursos do Swing Application Framework é possível injetar cores (java.awt.Color), fontes (java.awt.Font), ícones (javax.swing.ImageIcon), imagens (java.awt.Image), atalhos do teclado (javax.swing.KeyStroke), strings (java.lang.String), mensagens formatadas (java.text.MessageFormat), além dos tipos primitivos da linguagem Java e seus respectivos wrappers boolean, byte, short, int, long, float e double em propriedades e campos de componentes Swing.

Se precisar de injeção de recursos para outros tipos de dados, além dos citados acima, e para componentes SWT, utilize o projeto Fuse.

Observação: injeção de recursos é uma forma de IoC, assim como o é a injeção de dependência.

@Actions

Actions, em Swing, definem o comportamento do software sob o ponto de vista de seus usuários, implementando o padrão Command (GoF). São implementados a partir da interface javax.swing.Action ou da classe abstrata javax.swing.AbstractAction (mais sobre Actions em How to Use Actions e Using the Swing Action Architecture).

Com o Swing Application Framework, é possível implementar javax.swing.Action ao anotar um método com @Action. O framework irá utilizar esse método anotado para definir o método actionPerformed de javax.swing.Action e inicializar as propriedades dessa classe, criando uma instância de application.ApplicationAction.

Métodos anotados com @Action devem ter nenhum ou java.awt.event.ActionEvent como parâmetro; devem retornar void ou application.Task. No último caso, Task é executado pelo método actionPerformed de ApplicationAction.

A classe VProduto_Form aplica a teoria mencionada acima. Trata-se de um painel para cadastrar e visualizar os dados de uma entidade - o produto. Os métodos create e update, ao invés de serem definidos separadamente em uma subclasse de javax.swing.Action para cada método (e.g. CreateAction e UpdateAction), são escritos em VProduto_Form.

  1.  
  2. public final class VProduto_Form extends JPanel implements . . . {
  3.     private DefaultEntityModel<produto> entityModel;
  4.     // Demais campos e propriedades
  5.  
  6.     /** Necessário para construtores de interfaces gráfica, como o NetBeans Matisse. */
  7.     public VProduto_Form() {
  8.         super();
  9.         initComponents();
  10.     }
  11.  
  12.  
  13.     public VProduto_Form(DefaultEntityModel<Produto> entityModel) {
  14.         this();
  15.         this.entityModel = entityModel;
  16.     }
  17.  
  18.  
  19.     @Action public void create() {
  20.         if (entityModel == null) {
  21.             throw new IllegalStateException("defaulEntityModel não pode ser nulo");
  22.         }
  23.         entityModel.addEntity(getEntity());
  24.         clear();
  25.     }
  26.  
  27.  
  28.     @Action public void update() {
  29.         if (entityModel == null) {
  30.             throw new IllegalStateException("defaulEntityModel não pode ser nulo");
  31.         }
  32.         entityModel.updateEntity(updateSelectedEntity(produtoSelecionado));
  33.         clear();<
  34.     }
  35.     . . .
  36. }
  37.  

vproduto_form.png

Injeção de Recursos e @Actions

javax.swing.Action’s geralmente são utilizados para criar botões em barras de ferramenta, menus e outros componentes gráficos. Portanto, para serem úteis, precisam definir algumas propriedades, como ícones e descrições, além do método actionPerformed.

Recursos especificados num ResourceBundle de mesmo nome da classe action são injetados nas propriedades de ApplicationAction. As propriedades de ApplicationAction cujos valores podem ser injetados a partir de um arquivo de propriedade são:

  1.  
  2. Action.iconAction.text
  3. Action.shortDescription
  4. Action.longDescription
  5. Action.smallIcon
  6. Action.largeIcon
  7. Action.command
  8. Action.accelerator
  9. Action.mnemonic
  10. Action.displayedMnemonicIndex
  11.  

Continuando o exemplo acima, para injetar recursos nas propriedades dos ApplicationAction’s gerados pelo framework a partir dos métodos create e update de VProduto_Form, supondo que essa classe esteja no pacote gui.cadastro, cria-se um arquivo de propriedades gui.cadastro.resources.VProduto_Form.properties. Eis o conteúdo do arquivo:

  1.  
  2. create.Action.icon = save22.png
  3. create.Action.mnemonic = C
  4. create.Action.shortDescription = Cadastra entidade no banco de dados (Alt + C)
  5. update.Action.icon = edit22.png
  6. update.Action.mnemonic = U
  7. update.Action.shortDescription = Atualiza entidade no banco de dados (Alt + U)
  8.  

Os ícones save22.png e edit22.png também estão no pacote gui.cadastro.resources. Repare que o nome do método é utilizado para o binding dos recursos definidos nos arquivos de propriedades com as propriedades dos ApplicationAction’s.

E Finalmente. . .

A classe VProduto contém VProduto_Form e outros componentes gráficos, como a barra de ferramenta cujo dois dos botões serão implementados com o auxílio dos ApplicationAction’s criados pelo framework a partir dos métodos create e update de VProduto_Form.

  1.  
  2. public final class VProduto extends JXTitledPanel implements . . . {
  3.     private iboi.gui.cadastro.VCategoriaCliente_Form form;
  4.     // Demais campos e propriedades
  5.  
  6.     private void initComponents() {
  7.         toolBar = new javax.swing.JToolBar();
  8.         bCreate = new javax.swing.JButton();
  9.         bUpdate = new javax.swing.JButton();
  10.  
  11.         bCreate.setAction(getAction(VProduto_Form.class, form, "create"));
  12.         toolBar.add(bCreate);
  13.         bUpdate.setAction(getAction(VProduto_Form.class, form, "update"));
  14.         toolBar.add(bUpdate);
  15.     }
  16.  
  17.  
  18.     /* Método utilitário para obter Actions. */
  19.     private javax.swing.Action getAction(Class actionsClass, Object actionsObject, String actionName) {
  20.         return ApplicationContext.
  21.                     getInstance().
  22.                     getActionMap(actionsClass, actionsObject).
  23.                     get(actionName);
  24.     }
  25.     . . .
  26. }
  27.  

application.ApplicationActionMap, subclasse de javax.swing.ActionMap, contém chaves e valores correspondentes aos métodos anotados com @Action em uma classe action. A chave é o valor da propriedade name de @Action (por padrão, o nome do método), e o valor é o ApplicationAction que invoca o método anotado com @Action.

O método auxiliar getAction de VProduto é utilizado para obter o Action adequado. Basta fornecer os argumentos necessários: actionsClass é a classe action; actionsObject é uma instância dessa classe; e actionName o valor da propriedade name de @Action.

Os dois primeiros argumentos são necessários para acessar o ApplicationActionMap correto; o terceiro, para obter o Action requerido. Esse exemplo é interessante porque mostra que é possível definir um @Action numa classe e utilizá-lo em outras, algo que não está óbvio em outros tutoriais.

  1.  
  2. bCreate.setAction(getAction(VProduto_Form.class, form, "create"));
  3.  

vproduto.png

Cenas dos Próximos Capítulos

A terceira parte desse artigo mostrará funcionalidades adicionas ao utilizar @Action’s e tarefas executadas em background.

Veja Também:

Fontes e Referências Utilizadas:

Tags: , , , ,