Baixe o app para aproveitar ainda mais
Prévia do material em texto
Maurício Linhares de Aragão Junior – http://maujr.org/ AJAX em Java sem HTML nem JavaScript usando Thinwire Maurício Linhares de Aragão Junior Desenvolva aplicações web utilizando técnicas AJAX sem ter que escrever nenhuma linha de HTML ou JavaScript, utilizando a biblioteca de componentes para web Thinwire Introdução O Thinwire é um framework para o desenvolvimento de aplicações web utilizando técnicas AJAX baseado em componentes visuais, como as bibliotecas gráficas para desktop como Swing ou SWT. Nele, todo o seu código é escrito em Java e uma aplicação web é gerada baseada no que você programou. Diferentemente de outros frameworks para o desenvolvimento AJAX em Java, como o Google Web Toolkit, o Thinwire não utiliza de forma alguma geradores de código ou transformações, todo o código é sempre escrito em Java e executa normalmente dentro de qualquer servidor web Java. Antes de iniciar o tutorial você deve fazer o download do Thinwire na página oficial do projeto: http://www.thinwire.com/ , se já usa o Java 1.5 ou mais recente, pode baixar a versão própria para o Java 1.5 e basta apenas adicionar o JAR do “commons-fileupload” que está dentro do download do SDK do Thinwire, se você usa uma versão anterior ao Java 1.5, deve colocar todos os arquivos JAR que estão no SDK do Thinwire no seu classpath. Se você utiliza o Maven 2 para gerenciar o seus projetos Se você utiliza o Maven 2 para gerenciar os seus projetos, vai preceber que os arquivos do projeto existe uma pasta chamada “maven”, nela você vai encontrar uma pasta “project” com os arquivos do projeto do Maven 2 (incluindo um pom.xml) e uma pasta “repository” que contém a dependência do Thinwire configurada. A única configuração adicional que você precisa fazer é adicionar a dependência do Thinwire no seu repositório, para fazer isso, basta copiar a pasta que está dentro da pasta “repository” dos arquivos do projeto para dentro do seu repositório local do Maven 2. Se você não sabe o que é o Maven 2, pode saber mais no tutorial “Automatizando os seus projetos com o Maven 2” (http://guj.com.br/java.tutorial.artigo.185.1.guj ) Olá Mundo! Para começar os nossos estudos, vamos para o caso clássico de “Olá mundo”. Nós vamos criar uma janela (com o component Dialog) e nessa janela nós vamos adicionar um componente Label com o nosso texto. Listagem 1 – Olá Mundo com o Thinwire public class Main { public static void main(String[] args) { Application.current().getFrame().setTitle("Olá Mundo No Thinwire!"); Dialog dialog = new Dialog(); dialog.setTitle("Janela"); dialog.setBounds(100, 100, 400, 200); Label label = new Label(); label.setText("Olá Mundo Pelo Thinwire!"); label.setSize( 150, 50 ); dialog.getChildren().add(label); GUJ – http://www.guj.com.br – Página 1 http://www.guj.com.br/ http://maujr.org/ http://guj.com.br/java.tutorial.artigo.185.1.guj http://guj.com.br/java.tutorial.artigo.185.1.guj http://guj.com.br/java.tutorial.artigo.185.1.guj http://www.thinwire.com/ Maurício Linhares de Aragão Junior – http://maujr.org/ dialog.setVisible(true); } } O exemplo mostra um uso simples do Thinwire, na primeira linha nós acessamos a uma referência da aplicação corrente (a que está disponível para o usuário atual), depois acessamos o objeto Frame da aplicação, que é a tela (ou aba) do navegador que está mostrando a aplicação, e alteramos o título dele. Após essa alteração, criamos um objeto do tipo Dialog (uma janela), criamos um Label e adicionamos ele a janela e no fim do código nós alteramos a propriedade “visible” da janela para que ela torne-se visível. O código é extremamente simples e parecido com qualquer outro código de desenvolvimento de interfaces gráficas para desktops que você já tenha desenvolvido, a única diferença é que esse código é para o desenvolvimento de aplicações web que usam AJAX. As únicas partes diferentes que nós podemos perceber é quando em vez de usar o método “setSize()” na janela, nós usamos o método “setBounds()”, que posiciona o componente de forma absoluta em seu componente pai e não de forma relativa. Os dois primeiros parâmetros do método são as posições x e y iniciais no plano cartesiano representado pelo componente pai, que no nosso caso é o próprio Frame da janela do navegador. Se esses valores houvessem sido definidos em um componente com outro pai, esses valores teriam sido absolutos com relação ao atual pai do componente. Todos os componentes no Thinwire devem, obrigatoriamente, ter um tamanho, se você adicionar um componente sem colocar o seu tamanho ele simplesmente não vai ser desenhado na tela. Para podermos implantar essa aplicação web em um servidor web Java comum, nós precisamos configurar ela no web.xml da aplicação web, vejamos como seria essa configuração do web.xml: Listagem 2 – web.xml do primeiro exemplo <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>thinwire</servlet-name> <servlet-class>thinwire.render.web.WebServlet</servlet-class> <init-param> <param-name>styleSheet</param-name> <param-value>DefaultStyle</param-value> </init-param> <init-param> <param-name>mainClass</param-name> <param-value>org.maujr.thinwire.hello.Main</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>thinwire</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app> O web.xml em questão define um servlet do Thinwire que é responsável por carregar a nossa aplicação. Na configuração do servlet nós passamos as informações pelos valores dos “init-param”, o parâmetro “mainClass” diz qual é a classe que tem o método “public static void main (String[] args)”, é essa classe que define o ponto de entrada de nossa aplicação e é através dela que o Thinwire começa a inicialização do código. O segundo parâmetro, “styleSheet”, define o estilo visual que os componentes do Thinwire devem mostrar para o usuário, algo como um “skin” do seu sistema operacional. Nós definimos como estilo o “DefaultStyle” que é o estilo padrão da ferramenta. A pasta “DefaultStyle” deve estar no diretório raiz da GUJ – http://www.guj.com.br – Página 2 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ sua aplicação web para que ela possa ser utilizada pelo Thinwire dentro da sua aplicação. Os arquivos necessários para o “DefaultStyle” vem dentro do arquivo JAR do Thinwire e você também pode encontra- los nos arquivos do projeto do tutorial. Vejamos então a tela do nosso primeiro exemplo: Imagem 1 – Olá Mundo no Thinwire Labels e campos de entrada de texto Agora que já vimos o que é necessário configurar para fazer uma aplicação Thinwire executar, vejamos como utilizar os componentes de entrada de texto disponíveis no toolkit. Como campos de entrada para o usuário, nós temos: • Label – Campo de texto que normalmente serve para explicar um outro componente; • TextField – Componente que desenha um campo de texto simples de uma única linha, que pode ter uma máscara e também funcionar como um campo do tipo “password”, onde o texto escrito é escondido do usuário; • TextArea – Componehte que desenha um campo de texto que pode ter várias linhas de texto; • DateBox – Componente que desenha um calendário para que o usuário possa selecionar uma data (também já o DropDownDateBox que é um DateBox dentro de um componente DropDown); Vejamos um exemplo de código do uso desses componentes: Listagem 3 – Exemplo de labels e inputs public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Campos de Entrada"); GUJ – http://www.guj.com.br – Página 3 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de AragãoJunior – http://maujr.org/ Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Campos de Entrada"); dialog.setModal(false); dialog.setResizeAllowed(true); dialog.setBounds( 50, 50, 300, 200 ); Label labelInput = new Label(); labelInput.setBounds( 10, 10, 100, 20 ); labelInput.setText("InputText"); TextField inputField = new TextField(); inputField.setBounds(60, 10, 100, 20); Label labelPassword = new Label(); labelPassword.setBounds( 10, 40, 100, 20 ); labelPassword.setText("Password"); TextField inputPassword = new TextField(); /* Colocar a propriedade inputHidden com o valor TRUE faz * com que o componente não exiba o seu texto */ inputPassword.setInputHidden(true); inputPassword.setBounds(60, 40, 100, 20); Label labelMask = new Label(); labelMask.setBounds( 10, 70, 100, 20 ); labelMask.setText("Masked"); TextField inputMask = new TextField(); inputMask.setBounds(60, 70, 100, 20); /* Para criar um editor com máscara, basta dizer * qual o formato na propriedade editMask */ inputMask.setEditMask("##,##"); dialog.getChildren().add(labelInput); dialog.getChildren().add(inputField); dialog.getChildren().add(labelPassword); dialog.getChildren().add(inputPassword); dialog.getChildren().add(labelMask); dialog.getChildren().add(inputMask); dialog.setVisible(true); } Como você pode perceber, o exemplo segue a mesma lógica do anterior, nós criamos os componentes, damos uma posição a eles através da chamada do método “setBounds()”, se nós não tivéssemos feito as chamadas ao método “setBouds()” dos componentes, todos eles teriam sido desenhados na posição “0,0” do seu componente pai e apareciam todos um por cima dos outros, por isso é necessário posicionar todos absolutamente dentro dos seus componentes pai. No fim do exemplo nós adicionamos os componentes na janela e alteramos a propriedade “visible” da janela para que ela fique visível. As únicas partes diferentes deste código, são o início, onde alteramos algumas propriedades da janela (o objeto Dialog), nós fazemos com que ela seja não modal e que possa ter o seu tamanho alterado pelo usuário. Após isso, criamos um TextField alterando a sua propriedade “inputHidden” para transforma-lo em um campo que esconde o que o usuário digitou e no terceiro TextField nós alteramos a propriedade “editMask” para que ela formate a entrada que o usuário digitar. Imagem 2 – Imagem com o exemplo dos componentes de texto GUJ – http://www.guj.com.br – Página 4 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Vejamos como está o nosso arquivo de configuração agora: Listagem 5 – Arquivo de configuração das aplicações do Thinwire <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <servlet> <servlet-name>helloworld</servlet-name> <servlet-class>thinwire.render.web.WebServlet</servlet-class> <init-param> <param-name>mainClass</param-name> <param-value>org.maujr.thinwire.hello.Main</param-value> </init-param> <init-param> <param-name>styleSheet</param-name> <param-value>DefaultStyle</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>helloworld</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> GUJ – http://www.guj.com.br – Página 5 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ <servlet> <servlet-name>text</servlet-name> <servlet-class>thinwire.render.web.WebServlet</servlet-class> <init-param> <param-name>styleSheet</param-name> <param-value>DefaultStyle</param-value> </init-param> <init-param> <param-name>mainClass</param-name> <param-value>org.maujr.thinwire.text.Main</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>text</servlet-name> <url-pattern>/text</url-pattern> </servlet-mapping> </web-app> Agora nós temos duas aplicações do Thinwire configuradas. Devido a um bug ainda não resolvido na versão utilizada desse tutorial (a 1.2rc1), é necessário que sempre haja uma aplicação mapeada para a URL “/*”, então, se você vai colocar várias aplicações do Thinwire em uma única aplicação web, lembre- se sempre de adicionar uma delas mapeada para o contexto raiz em “/*”, as outras aplicações podem ter as suas URLs normalmente (o mapeamento genérico não vai afetar o seu funcionamento). Você também pode simplesmente mapear o mesmo servlet para dois caminhos de URL distintos. Vejamos agora um exemplo que use TextBoxes e os campos de entrada de data: Listagem 4 – Exemplos de labels, TextBoxes e DateBoxes public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Campos de Entrada"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Campos de Entrada"); dialog.setModal(false); dialog.setResizeAllowed(true); dialog.setBounds( 50, 50, 400, 400 ); Label labelArea = new Label(); labelArea.setBounds( 10, 10, 100, 20 ); labelArea.setText("TextArea"); final Label labelResult = new Label(); labelResult.setBounds( 170, 10, 100, 100 ); labelResult.setWrapText(true); labelResult.setText(""); TextArea textArea = new TextArea(); textArea.setBounds(60, 10, 100, 100); textArea.addPropertyChangeListener( TextComponent.PROPERTY_TEXT , new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { labelResult.setText( ((TextComponent) ev.getSource()).getText() ); } }); Label labelDate = new Label(); labelDate.setBounds(10, 120, 100, 20); GUJ – http://www.guj.com.br – Página 6 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ labelDate.setText("Date"); DateBox dateBox = new DateBox(); dateBox.setBounds(60, 120, 100, 150); Label labelDateDropDown = new Label(); labelDateDropDown.setText("DropDate"); labelDateDropDown.setBounds(10, 280, 100, 20); DropDownDateBox dropDownDateBox = new DropDownDateBox(); dropDownDateBox.setBounds(60, 280, 100, 20); dialog.getChildren().add(labelArea); dialog.getChildren().add(textArea); dialog.getChildren().add(labelResult); dialog.getChildren().add(labelDate); dialog.getChildren().add(dateBox); dialog.getChildren().add(labelDateDropDown); dialog.getChildren().add(dropDownDateBox); dialog.setVisible(true); } Neste exemplo, temos de novo os componentes que desenham calendários para que o usuário possa selecionar uma data. Além dos componentes de data, nós também criamos um “listener”, que é um objeto que responde a um evento, e registramos ele no evento de alteração da propriedade “text” do TextArea, pois assim, a cada vez que alguém digitar alguma coisa naquele componente o código vai receber o evento e vai escrever o atual valor do TextArea em um Label que está ao seu lado. Imagem 3 – Imagem do exemplo de uso de TextAreas e Dates GUJ – http://www.guj.com.br – Página 7 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Lidando com botões O Thinwire define os três tipos mais comuns de botões, o botão comum de clicar, o checkbox, que é um botão que serve como um marcador e os botões de radio, que funcionam como uma lista onde você pode selecionar um único valor. Vejamos o nosso exemplo de código: Listagem 5 – Exemplo do uso de botões public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Botões"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Botões"); dialog.setModal(false); dialog.setResizeAllowed(true); dialog.setBounds( 50, 50, 400, 400 ); Button button = new Button(); button.setText("Clique Aqui!"); button.setBounds(50, 50, 100, 30); button.addActionListener( Button.ACTION_CLICK , new ActionListener() { public void actionPerformed(ActionEvent ev) { Button button = (Button) ev.getSource(); if ( "Clique Aqui!".equals( button.getText()) ) { GUJ – http://www.guj.com.br – Página 8 http://www.guj.com.br/http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ button.setText("Clique Ali!"); } else { button.setText("Clique Aqui!"); } } } ); CheckBox checkBox1 = new CheckBox(); checkBox1.setText("Opção 1"); checkBox1.setBounds(50, 90, 100, 30); CheckBox checkBox2 = new CheckBox(); checkBox2.setText("Opção 2"); checkBox2.setBounds(50, 130, 100, 30); CheckBox checkBox3 = new CheckBox(); checkBox3.setText("Opção 3"); checkBox3.setBounds(50, 170, 100, 30); RadioButton radio1 = new RadioButton(); radio1.setText("Radio 1"); radio1.setBounds(50, 230, 100, 30); RadioButton radio2 = new RadioButton(); radio2.setText("Radio 2"); radio2.setBounds(50, 270, 100, 30); RadioButton.Group radioGroup = new RadioButton.Group(); radioGroup.add(radio1); radioGroup.add(radio2); dialog.getChildren().add(button); dialog.getChildren().add(checkBox1); dialog.getChildren().add(checkBox2); dialog.getChildren().add(checkBox3); dialog.getChildren().add(radio1); dialog.getChildren().add(radio2); dialog.setVisible(true); } A criação de componentes é igual a que nós já havíamos visto antes, a única diferença que nós podemos notar nesse código é a forma que o ActionListener é implementado. Ele não é registrado simplesmente para o evento de clique do botão, mas para um tipo de clique, que é o clique simples, se o evento fosse para o clique duplo, o evento utilizado seria o Button.ACTION_DOUBLE_CLICK. Então, no nosso caso, a cada vez que o usuário clicar no botão, o evento vai ser chamado. Outro detalhe importante é o objeto RadioButton.Group, que serve para agrupar um conjunto de RadioButtons, é através dele que nós garantimos que apenas um dos RadioButtons está selecionado em uma interface. Após criar os RadioButtons nós adicionamos eles no Group para que eles possam ser utilizados corretamente. Imagem 4 – Exemplo do uso de botões GUJ – http://www.guj.com.br – Página 9 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Painéis com abas - TabFolders Painéis com abas são comuns em aplicações, eles costumam representar opções de configurações, vários documentos abertos (como em navegadores que tem suporte a abas). Vejamos como criar painéis com abas no Thinwire: Listagem 6 – Exemplo do uso de TabFolders public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Painéis com Abas"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Painéis com Abas"); dialog.setBounds(100, 100, 400, 400); TabFolder tabFolder = new TabFolder(); tabFolder.setSize(300, 300); TabSheet firstTabSheet = new TabSheet();; firstTabSheet.setText("Aba 1"); Label label = new Label(); label.setText("Label na primeira Aba"); label.setSize(150, 30); firstTabSheet.getChildren().add(label); TabSheet secondTabSheet = new TabSheet(); secondTabSheet.setText("Aba 2"); Button button = new Button(); button.setText("Button na segunda aba"); button.setSize(150, 30); secondTabSheet.getChildren().add(button); tabFolder.getChildren().add(firstTabSheet); tabFolder.getChildren().add(secondTabSheet); dialog.getChildren().add(tabFolder); dialog.setVisible(true); } GUJ – http://www.guj.com.br – Página 10 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ O componente que representa o “container” de abas é o TabFolder, cada aba é um objeto do tipo TabSheet. Para adicionar um componente em uma das abas, basta usar a referência para o TabSheet no qual você deseja adicionar novos componentes e adicioná-los a a sua lista. Você pode selecionar uma aba específica usando o método “setCurrentIndex()” da classe TabFolder, passando como parâmetro o índice da TabSheet que você deseja selecionar. Imagem 5 – Exemplo do uso de painéis com abas Mostrando dados na forma de tabelas Exibir informação na forma de tabelas é lugar comum na maioria das aplicações desenvolvidas, seja qual for o seu trabalho real. Tabelas bem organizadas facilitam até mesmo o entendimento do que está sendo mostrado por parte dos usuários, então um bom framework de interface não poderia deixar de ter um componente tabela para mostrar informações. O componente que representa as tabelas no Thinwire é o GridBox, que além de mostrar os dados de forma tabular, também torna possível a seleção de suas linhas, vejamos um exemplo do uso desse componente: Listagem 7 – Exemplo do uso de tabelas public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Tabela"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Tabela"); dialog.setModal(false); dialog.setResizeAllowed(true); dialog.setBounds( 100, 100, 400, 400 ); GridBox table = new GridBox(); table.setVisibleHeader(true); Column firstColumn = new Column(); firstColumn.setName("ID"); table.getColumns().add(firstColumn); Column secondColumn = new Column(); GUJ – http://www.guj.com.br – Página 11 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ secondColumn.setName("Nome"); table.getColumns().add(secondColumn); Column thirdColumn = new Column(); thirdColumn.setName("Preço"); table.getColumns().add(thirdColumn); for ( int x = 0; x < NOMES.length; x++ ) { Row row = new Row(); row.add( x); row.add( NOMES[x]); row.add( x * 10); table.getRows().add(row); } table.setSize(250, 250); dialog.getChildren().add(table); dialog.setVisible(true); } Para criar uma tabela no Thinwire nós precisamos apenas criar o componente GridBox, criar os objetos GridBox.Column, que representam as colunas da tabela e depois popular a tabela com os objetos GridBox.Row, cada objeto Row representa uma linha de informações na tabela, pra preencher a linha você deve usar os métodos “add()” disponíveis no objeto Row. A cada objeto Row que você adicionar, uma nova linha vai ser mostrada na tabela. A única configuração além da normal que nós fazemos na nossa tabela é a chamada do método “setVisibleHeaders()”, que faz com que as colunas sejam mostradas como cabeçalhos na tabela, se você não chamar esse método, os cabeçalhos de coluna não vão ser mostrados na sua tabela, ela já vai começar mostrando os dados da primeira linha que você criou. O GridBox também apresenta outras características, como a possibilidade de se selecionar as linhas da tabela (e de se registrar para o evento de seleção da tabela). Para saber qual é a atual linha selecionada basta chamar o método “getSelectedRow()”, que retorna a linha que está atualmente selecionada na tabela. Vejamos como registrar um evento que seja chamado na hora que o usuário selecionar uma linha no GridBox: Listagem 8 – Exemplo de listener registrado para a seleção de linha table.addPropertyChangeListener( GridBox.Row.PROPERTY_ROW_SELECTED, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent ev) { Row row = (Row) ev.getSource(); } } ); Nós registramos o listener no GridBox (o objeto “table”), mas a propriedade registrada é a propriedade do objeto linha, então o objeto que vai ser enviado como “fonte” do evento é o objeto que representa a linha atualmente selecionda. Imagem 6 – Exemplo de tabela GUJ – http://www.guj.com.br – Página 12 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Árvores Mostrar informações de forma hierárquica utilizando árvores é uma necessidade comum e o Thinwire oferece suporte a esse tipo de representação através do componente Tree e dos objetos Tree.Item, vejamos um exemplo de uso de árvores para mostrar informações de forma hierárquica: Listagem 9 – Árvores no Thinwire public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Árvore"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Árvore"); dialog.setModal(false); dialog.setResizeAllowed(true); dialog.setBounds( 200, 200, 400, 400 ); Tree tree = new Tree(); tree.setSize(300,300); tree.setRootItemVisible(false);Item firstItem = new Item(); firstItem.setText("Item 1"); tree.getRootItem().getChildren().add(firstItem); Item secondItem = new Item(); secondItem.setText("Item 2"); tree.getRootItem().getChildren().add(secondItem); GUJ – http://www.guj.com.br – Página 13 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Item thirdItem = new Item(); thirdItem.setText("Item 3"); tree.getRootItem().getChildren().add(thirdItem); Item firstInternalItem = new Item(); firstInternalItem.setText("Internal 1"); firstItem.getChildren().add(firstInternalItem); Item secondInternalItem = new Item(); secondInternalItem.setText("Internal 1"); firstItem.getChildren().add(secondInternalItem); dialog.getChildren().add(tree); dialog.setVisible(true); } Para criar uma árvore, você primeiro precisa de um objeto árvore, depois você precisa criar objetos do tipo Tree.Item para representar cada um dos itens da árvore. Se você deseja que um dos itens tenha sub-itens, basta criar os objetos Tree.Item correspondentes e adicioná-los no item que você deseja que tenha os sub-itens. Imagem 7 – Exemplo de Árvore Barras de menu Janelas com barras de menu são uma necessidade comum em aplicações, para que o usuário possa selecionar facilmente as ações que deseja realizar. O Thinwire define o componente Menu para que essas barras de menu possam ser criadas e apenas os componentes que implementam a interface Window podem receber barras de menu, atualmente, os dois componentes que implementam Window são o componente Frame (que é a janela do navegador) e o componente Dialog, que é a janela do Thinwire. Vejamos um exemplo do uso de menus no Thinwire: Listagem 10 – Exemplo da criação de menus public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Menu"); Dialog dialog = new Dialog(); dialog.setTitle("Exemplo de Menu"); dialog.setBounds(100, 100, 200, 200); Menu menu = new Menu(); //acessando o item raiz do menu GUJ – http://www.guj.com.br – Página 14 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Item rootItem = menu.getRootItem(); Item fileItem = new Item(); fileItem.setText("Arquivo"); rootItem.getChildren().add(fileItem); Item newFileItem = new Item(); newFileItem.setText("Novo"); fileItem.getChildren().add(newFileItem); Item newProjectItem = new Item(); newProjectItem.setText("Projeto"); newFileItem.getChildren().add(newProjectItem); Item exitFileItem = new Item(); exitFileItem.setText("Sair"); fileItem.getChildren().add(exitFileItem); Item editItem = new Item(); editItem.setText("Editar"); rootItem.getChildren().add(editItem); Item helpItem = new Item(); helpItem.setText("Ajuda"); rootItem.getChildren().add(helpItem); Item openHelpItem = new Item(); openHelpItem.setText("Abrir Ajuda"); helpItem.getChildren().add(openHelpItem); menu.addActionListener(Component.ACTION_CLICK, new ActionListener() { public void actionPerformed(ActionEvent event) { MessageBox.confirm( "Selecionando um item do menu", String.format( "The selected menu item was -> %s", event.getSource())); } } ); // Dialogs e Frames implementam a interface Window, //portanto ambos podem ter uma barra de menu dialog.setMenu(menu); dialog.setVisible(true); } Um Menu no Thinwire é um componente que tem vários objetos Menu.Item como seus filhos. Para criar um menu você precisa primeiro criar um objeto do tipo Menu, acessar a referência ao Menu.Item raiz desse menu através do seu método “getRootItem()” e depois basta adicionar os itens nesse root item e em seus itens filhos (de forma parecida com a do componente árvore, a diferença aqui é que esse item raiz nunca é desenhado na tela). Para registrar eventos em um Menu, você normalmente vai registrar um “ActionListener” no menu em questão. O objeto que é retornado como fonte dos eventos (através do método “getSource()” do objeto ActionEvent) é o Menu.Item que foi selecionado pelo usuário, então no tratamento do evento você deve implementar uma lógica saiba identificar qual item foi selecionado e o que deve ser feito. GUJ – http://www.guj.com.br – Página 15 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Um menu não é “adicionado” a um componente através da coleção de filhos (o método “getChildren()”), ele é uma propriedade do objeto no qual vai ser colocado. Menus são sempre colocados através do método “setMenu()” no objeto que você deseja que o menu apareça. Lembre-se que apenas objetos que implementam a interface Window do Thinwire podem ter barras de menu. Imagem 8 – Exemplo de Menu Fazendo upload de arquivos Fazer upload de arquivos em aplicações web costuma ser um trabalho complicado, com a adição de bibliotecas que não são padrão e o uso de objetos que não fazem, necessariamente, parte de uma aplicação web. No Thinwire, toda a lógica de upload de arquivos é abstraída do programador, que simplesmente cria o componente e espera que o usuário selecione o arquivo para ser enviado. Vejamos como fazer upload de arquivos com o Thinwire: Listagem 11 – Upload de arquivos com o Thinwire public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Upload de Arquivo"); Dialog dialog = new Dialog("Exemplo de Upload de Arquivo"); dialog.setBounds(0, 0, 400, 400); final FileChooser fileChooser = new FileChooser(); fileChooser.setSize(300, 30); Button uploadButton = new Button(); uploadButton.setText("Enviar Arquivo"); uploadButton.setBounds(0, 40, 100, 30); final Label label = new Label(); label.setBounds(0, 80, 300, 30); uploadButton.addActionListener( Component.ACTION_CLICK , new ActionListener() { public void actionPerformed(ActionEvent event) { FileInfo fileInfo = fileChooser.getFileInfo(); GUJ – http://www.guj.com.br – Página 16 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ File file = new File(new File(fileInfo.getName()).getName()); System.out.printf("AbsolutePath -> %s%n", file.getAbsolutePath()); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } fileInfo.saveToFile( file ); label.setText("Salvo em -> " + file.getAbsolutePath()); } }); dialog.getChildren().add(fileChooser); dialog.getChildren().add(uploadButton); dialog.getChildren().add(label); dialog.setVisible(true); } Na nossa interface, nós criamos o componente que faz o upload de arquivos, o FileChooser e criamos mais um botão, que vai ser clicado pelo usuário na hora que ele quiser que o arquivo seja enviado. O componente FileChooser é um campo de upload comum de aplicações web, mas nós não precisamos mais nos preocupar em receber requisições ou fazer qualquer outra coisa, basta esperar que o usuário selecione o arquivo e recebê-lo, vejamos como isso acontece: Listagem 12 – Tratamento do evento public void actionPerformed(ActionEvent event) { FileInfo fileInfo = fileChooser.getFileInfo(); File file = new File(new File(fileInfo.getName()).getName()); try { file.createNewFile(); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); } fileInfo.saveToFile( file ); label.setText("Salvo em -> " + file.getAbsolutePath()); } Nós acessamos a propriede “fileInfo” do objeto FileChooser, criamos um arquivo onde o conteúdo que está sendo enviado vai ser gravado e chamamos o método “saveToFile()” do objeto FileInfo, que vai escrever todo o conteúdo do arquivo enviado no arquivo que nós criamos. Imagem 8 – Upload de arquivos GUJ – http://www.guj.com.br – Página 17 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Simulando Listas e combos no Thinwire Após mostrar tantos componentes, alguns comuns outros nem tanto, você deve ter sentido falta de dois componentes que são lugar comum em aplicações web e desktop, que são as listas de seleção e os “combo boxes”.No Thinwire, por uma decisão da própria equipe de desenvolvimento, esses componentes não “existem” de fato, mas isso não quer dizer que nós não possamos ter componentes que façam o mesmo. Apenas não existem classes, como TextField, que implementam as funcionalidades desses componentes, mas elas podem ser conseguidas com o uso dos componentes padrão da biblioteca. Vejamos então como nós poderíamos criar esses componentes: Listagem 13 – Exemplo de listas e combos public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Listas e Combos"); Dialog dialog = new Dialog("Exemplo de Listas e Combos"); dialog.setBounds(200, 200, 600, 400); GridBox listaSelecaoSimples = new GridBox(); listaSelecaoSimples.setVisibleHeader(false); listaSelecaoSimples.setBounds(0, 0, 100, 100); fillGridBox(listaSelecaoSimples); GridBox listaSelecaoMultipla = new GridBox(); listaSelecaoMultipla.setVisibleCheckBoxes(true); listaSelecaoMultipla.setVisibleHeader(false); listaSelecaoMultipla.setFullRowCheckBox(true); listaSelecaoMultipla.setBounds(0, 120, 100, 100); fillGridBox(listaSelecaoMultipla); DropDownGridBox comboBox = new DropDownGridBox(); comboBox.getComponent().setVisibleHeader(false); comboBox.setBounds(0, 240, 100, 30); fillGridBox( comboBox.getComponent() ); GUJ – http://www.guj.com.br – Página 18 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ dialog.getChildren().add(listaSelecaoSimples); dialog.getChildren().add(listaSelecaoMultipla); dialog.getChildren().add(comboBox); dialog.setVisible(true); } Como você pôde ver pelo código, as listas de seleção única e múltipla são apenas objetos do tipo GridBox que tem apenas uma coluna e que não mostram os seus cabeçalhos (a propriedade “visibleHeaders” está como “false”). A única diferença entre a de seleção simples da de seleção múltipla é que a de seleção múltipla tem seu estado mantido por um conjunto de checkboxes e não apenas pela seleção da linha do componente. Para saber qual a linha selecionada no de seleção simples, use o método “getSelectedRow()”, para saber quais as linhas selecionadas no de seleção múltipla, você deve usar o método “getCheckedRows()”. O combo box no fim do exemplo é apenas um GridBox dentro de um componente DropDown (que é um componente que desenha o seu componente filho dentro de uma caixa “drop-down”) que funciona exatamente da mesma forma que um GridBox comum funcionaria. Imagem 9 – Exemplo de listas e combos Componentes diversos Vejamos agora alguns componentes diversos do Thinwire que não tem um uso freqüente, mas que podem simplificar tarefas que seriam complexas de se fazer com componentes padrão. Vejamos alguns exemplos de uso desses componentes: Componente Image O componente Image mostra uma imagem qualquer no lugar onde ele for adicionado. As imagens podem ser de qualquer tipo que o navegador puder aceitar. Listagem 13 – Componente Image public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Imagem"); Dialog dialog = new Dialog(); dialog.setTitle("Janela com Imagem"); dialog.setBounds(100, 100, 400, 400); Image image = new Image("images/logo-guj.gif"); image.setSize(289, 70); dialog.getChildren().add(image); GUJ – http://www.guj.com.br – Página 19 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ dialog.setVisible(true); } Parar criar um componente Image, precisamos apenas passar o caminho para a imagem que vai ser mostrada no seu construtor. Os caminhos que podem ser passados são caminhos relativos ao contexto da aplicação (que é o caso desse exemplo), o caminho completo no sistema de arquivos ou o caminho dentro do classpath, caminhos dentro do classpath deve ter como prefixo a palavra “classpath”, como em “class:///thinwire.ui.layout.SplitLayout/resources/Image.png”. Imagem 9 – Exemplo do componente Image Componente ProgressBar Uma ProgressBar (ou barra de progresso) normalmente mede a execução de alguma tarefa longa ou o tamanho de alguma coisa que tem tendência a se alterar durante a execução da aplicação, ela normalmente vai “enchendo” conforme as ações vão acontecendo. Vejamos um exemplo: Listagem 14 – Componente ProgressBar public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Barra de Progresso"); Dialog dialog = new Dialog(); dialog.setTitle("Janela com Barra de Progresso"); dialog.setBounds(100, 100, 400, 400); final ProgressBar progressBar = new ProgressBar(100, 0); progressBar.setSize(100, 30); Button fillButton = new Button(); fillButton.setText("Preencher"); fillButton.setBounds(0, 40, 50, 30); fillButton.addActionListener(Component.ACTION_CLICK, new ActionListener() { public void actionPerformed(ActionEvent event) { for ( int x = 0; x < 100; x ++ ) { progressBar.setCurrentIndex(x); try { GUJ – http://www.guj.com.br – Página 20 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }); Button resetButton = new Button(); resetButton.setText("Zerar"); resetButton.setBounds(60, 40, 50, 30); resetButton.addActionListener(Component.ACTION_CLICK, new ActionListener() { public void actionPerformed(ActionEvent event) { progressBar.setCurrentIndex(0); } }); dialog.getChildren().add(progressBar); dialog.getChildren().add(fillButton); dialog.getChildren().add(resetButton); dialog.setVisible(true); } Nesse exemplo, nós temos uma ProgressBar e dois botões, um que vai enchendo a barra e outro que faz com que o seu contador volte a zero. No construtor da barra nós passamos o seu tamanho e o índice inicial dela (é o valor no qual ela vai se iniciar), para alterar o valor do seu tamanho, basta alterar a propriedade “currentIndex” da barra. Imagem 10 – Exemplo de ProgressBar GUJ – http://www.guj.com.br – Página 21 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Componente Slider O componente Slider é utilizado para facilitar a seleção de números dentro de um intervalo, como escolher os números entre zero e cem. Vejamos um exemplo de uso do Slider: Listagem 15 – Exemplo de Slider public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Slider"); Dialog dialog = new Dialog(); dialog.setTitle("Janela Slider"); dialog.setBounds(100, 100, 400, 400); final Slider slider = new Slider( 200, 0 ); slider.setSize(100, 30); final TextField textField = new TextField(); textField.setBounds(0, 40, 50, 30); slider.addPropertyChangeListener( RangeComponent.PROPERTY_CURRENT_INDEX , new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { textField.setText( new Integer(slider.getCurrentIndex()).toString() ); } }); dialog.getChildren().add(slider); dialog.getChildren().add(textField); dialog.setVisible(true); GUJ – http://www.guj.com.br – Página 22 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ } O Slider funciona com a definição de um intervalo, da mesma forma que o ProgressBar, a diferença é que o Slider é um componente que o próprio usuário seleciona e diz qual é o valor. Imagem 11 – Exemplo de Slider Componente WebBrowser O componente WebBrowser cria um navegador na tela do usuário, permitindo de que da sua aplicação, ele possa abrir outras páginas e até mesmo outras aplicações. Vejamos um exemplo de seu uso: Listagem 16 – Exemplo do componente WebBrowser public static void main(String[] args) { Application.current().getFrame().setTitle("Exemplo de Navegador"); Dialog dialog = new Dialog("Exemplo de Navegador"); dialog.setBounds(0, 0, 800, 600); WebBrowser webBrowser = new WebBrowser(); webBrowser.setBounds(20, 20, 750, 550); webBrowser.setLocation("http://www.guj.com.br/"); dialog.getChildren().add(webBrowser);dialog.setVisible(true); } Como você pode ver, basta criar o componente e dizer qual página você deseja que ele carregue, todo o resto do trabalho é feito pelo próprio componente. Imagem 12 – Exemplo do componente WebBrowser GUJ – http://www.guj.com.br – Página 23 http://www.guj.com.br/ http://maujr.org/ Maurício Linhares de Aragão Junior – http://maujr.org/ Referências Site oficial do projeto – http://www.thinwire.com/ Conclusão Como você pôde perceber, utilizar o Thinwire para o desenvolvimento de aplicações web é extremamente simples e ainda lhe dá como resultado aplicações com interfaces ricas e responsivas para o seus clientes usando técnicas AJAX para envio e atualização d conteúdo. Entretanto, o Thinwire não é a opção correta se você está buscando desenvolver páginas na web, ele é uma boa escolha se a sua necessidade é desenvolver aplicações com interfaces complexas, se o seu problema pode ser resolvido simplesmente com algumas páginas e formulários, usar outras tecnologias, como Servlets, JSF ou JSP, podem ser uma escolha mais acertada. Tenha em vista também que todo o estado da sua aplicação é mantida pelo servidor, então não espere que uma aplicação desenvolvida no Thinwire possa ser instalada num servidor “simples” e servir a aplicação simultaneamente para milhares de pessoas. Os desenvolvedores do projeto tem clientes que já chegam a até oitocentos usuários simultâneos, mas isso é extremamente relativo a o tamanho da aplicação e a máquina servidora que está a servindo, então não pense em fazer uma aplicação dessas rodando em um único servidor se você espera ter cinqüenta mil pessoas penduradas naquele seu único servidor. Faça testes de carga e integração cedo para descobrir se esta realmente é a abordagem correta, pra que você não tenha que esquentar a cabeça com problemas de lentidão depois que a aplicação tiver sido implantada e começado a ser utilizada pelo seu cliente. Maurício Linhares de Aragão Junior (mauricio.linhares@gmail.com) é graduando em Desenvolvimento de Sistemas para a Internet e Comunicação Social, com Habilitação em Jornalismo, desenvolvedor da Phoebus Tecnologia ( http://www.phoebus.com.br/ ), consultor e instrutor independente, instrutor parceiro da Synapse Tech Cursos ( http://stcursos.com/ ), membro da equipe administrativa do PBJUG ( http://pbjug.org/ ) e colaborador dos fóruns do GUJ ( http://guj.com.br/ ). GUJ – http://www.guj.com.br – Página 24 http://www.guj.com.br/ http://guj.com.br/ http://pbjug.org/ http://stcursos.com/ http://www.phoebus.com.br/ mailto:mauricio.linhares@gmail.com http://maujr.org/ http://www.thinwire.com/
Compartilhar