Criando uma simples Janela Swing
Este tutorial é bem básico, feito para quem não tem prática na criação de interfaces gráficas em java.
Certo, queremos criar uma simples interface com o usuário que seja uma janela padrão (com funções de minimizar, maximizar e fechar) contendo um caixa de texto dentro (digamos que seja um início para o nosso notepad em java :00ps: ).
Sigamos o raciocínio: primeiro fazemos a janela. Como em java trabalhamos com objetos em tudo então precisamos de um objeto que [b]seja uma[/b] janela. Então nossa classe de janela vai herdar da classe de janela do Swing (prefira-o ao awt, na maioria dos casos):
Bem, jé temos uma classe que cria objetos que são janelas. Agora precisamos construir a infra-estrutura da janela, modificando-a e adicionando coisas. Isso faz parte do processo de construção da janela e nenhum lugar melhor para isso que no [b]construtor[/b] da classe!
Antes uma pequena pausa para explicar um detalhe. As API gráficas do java provêm dois tipos de componentes visuais: componentes propriamente ditos e containers.
[list] Um componente é uma peça (widget) que tem alguma função na interface.
Um container é um componente que tem a capacidade de abrigar outros componentes (conteiners possuem um método [i]add[/i](), que recebe qualquer componente como parâmetro e o adiciona a sua lista de componentes "abrigados").[/list]
Sem entrar em muitos méritos (esse é um tutorial curto), o swing tem uma característica interessante adicional: todos os componentes swing, todos [b]mesmo[/b] são também containers (possuem um método [i]add[/i]())! :shock:
Retomando a nossa trilha agora: nosso próximo objetivo é abrigar uma caixa de texto na janela. Para isso, simplesmente criaríamos um objeto JTextArea (é o componente visual para grandes áreas de texto não formatado (como no notepad do windows) no swing) e o passaríamos para o método [i]add[/i]().
Mas há um pulo do gato aí! Até o Java 1.4, não era possível adicionar componentes gráficos [b]diretamente[/b] em um JFrame. Isso pq ele é um container especial que só permite um número limitado de componentes (containers) abrigados nele. Em um JFrame existem alguns containers "invisíveis" sobrepostos. O container mais superficial é chamado de "[color=red:ec9b2e8e19][b]contentPane[/b][/color:ec9b2e8e19]" (painel de conteúdo) e é a superfície na qual os componentes devem ser dispostos.
Usar o método [i]add[/i]() do JFrame faria com que nosso componente ficasse embaixo do [color=red:ec9b2e8e19][b]contentPane[/b][/color:ec9b2e8e19], sem efeito visual. Por isso, no J2SE1.4 para trás, o compilador reclama com vc. se isso for feito.
Mas então como raios eu adiciono o componente? Simples, obtenha uma referência ao contentPane e use o método [i]add[/i]() desse container:
No Java 1.5 (J2SE5.0 - Tiger), se vc. usa o método add() do JFrame ([i]this.add(texto)[/i]), o compilador já supõe que vc. quer adicionar um componente no contentPane e gera um bytecode equivalente a [i]this.getContentPane().add(texto)[/i]. Boa notícia, não? Mas lembre-se: só funciona da versão 5.0 do Java em diante.
Nosso objetivo está quase pronto, já temos nossa janela. Agora só precisamos exibir a mesma. Uma arquitetura melhor seria criar outra classe executável para usar a nossa janela, inicializando a aplicação criando e exibindo. Mas para sermos simplistas, vamos usar essa classe mesmo, tornando-a uma classe funcional (executável).
Ponha isso pra rodar e vc. vai ter... [b]nada[/b]! Vc. criou o objeto da janela, mas em momento algum disse para torná-lo visivel ao usuário. Acrescente então no método main() a linha:
Ao rodar isso vc. obtém sua janela... Mas ops! ela fica pequenininha lá no canto esquerdo. Vc. tem que definir um tamanho para sua janela!
Acrescente a linha:
Dica: Faça isso antes de exibir sua janela. Assim a JVM não precisa enviar mensagens ao Sistema Operacional para redimensionar a janela. Defina o tamanho antes (deixando que os componentes dentro da janela se organizem para o tamanho da mesma) e mostre depois!
Obs.: Ao invés do setSize(), vc. também pode utilizar o método pack() (sem parâmetros dessa vez) para que ele configure a janela com o melhor parâmetro que abrigue todos os componentes, de acordo com seus tamanhos. Em nosso exemplo não vai adiantar nada, pq não definimos um tamanho (preferencial ou específico) para o JTextArea.
Enfim, eis o código que funciona perfeitamente:
E é o fim de nosso pequeno tutorial. Esse é um ponto de partida para evoluir nossas habilidades com alguns temas mais avançados como:
[list]1- Como fazer a minha aplicação interagir com o usuário (tratamento de eventos)?
2- Como colocar uma janela dentro da outra (MDI - Multiple Document Interface)?
3- Como construir um menu e barras de ferramentas?
4- Como tornar meus comandos de aplicação (novo, abrir, salvar, e outros mais complexos) portáveis para outras aplicações que eu fizer?
5- A minha GUI está um pouco feia ou inadequada para usuários acostumados a um determinado Sistema Operacional... Há como mudar a aparência e/ou o comportamento dela (Look And Feel - Aparência e Comportamento)?
6- Como evitar que a janela "congele" quando a aplicação executar uma ação "pesada" (execução multitarefa)?
7- Como eu integro minha aplicação ao "icon tray" do Windows?[/list]
Esses e outros tópicos podem ser explorados nos próximos tutoriais. Qual o próximo assunto que vc. gostaria de ver explorado? Opine!
E, da próxima vez, considere fazer sua nova aplicação como uma aplicação desktop, oferecendo mais recursos visuais e integração ao seu usuário. Afinal além de cada vez mais rápido a cada nova versão, java é portável, aproveite isso! :!:
import javax.swing.JFrame;
public class MeuNote extends JFrame{
. . .
}
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class MeuNote extends JFrame{
/* Componentes devem estar no contexto da instância,
para que possam ser acessados em todos os métodos
não-estáticos da classe
*/
private JTextArea texto = new JTextArea();
public MeuNote(){
//Define o título da janela
super("Meu Notepad");
this.montaJanela();
}
private void montaJanela(){
this.getContentPane().add(texto);
}
. . .
}
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class MeuNote extends JFrame{
/* Componentes devem estar no contexto da instância,
para que possam ser acessados em todos os métodos
não-estáticos da classe
*/
private JTextArea texto = new JTextArea();
public MeuNote(){
//Define o título da janela
super("Meu Notepad");
this.montaJanela();
}
private void montaJanela(){
this.getContentPane().add(texto);
}
public static void main(String[] args){
//Cria objeto:
MeuNote janela = new MeuNote();
}
}
janela.setVisible(true);
/* A medida de tamanho é em pixels por polegada, igual a da resolução da sua tela */
janela.setSize(640,480);
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class MeuNote extends JFrame{
/* Componentes devem estar no contexto da instância,
para que possam ser acessados em todos os métodos
não-estáticos da classe
*/
private JTextArea texto = new JTextArea();
public MeuNote(){
//Define o título da janela
super("Meu Notepad");
this.montaJanela();
}
private void montaJanela(){
this.getContentPane().add(texto);
}
public static void main(String[] args){
//Cria objeto:
MeuNote janela = new MeuNote();
janela.setSize(640,480);
janela.setVisible(true);
}
}
Claudio Silva
Curtidas 1
Respostas
Ronaldo Luiz
09/04/2009
O assunto que eu gostaria de ver explorado é de MDI e também de telas de modelo para telas de cadastro (com botões incluir, salvar, excluir, atualizar...) e também para telas de pesquisa.
Se possível, gostaria de ver alguma técnica para facilitar a obtenação de dados por consulta.
Ex: suponha que eu tenha que informar um cliente em determinado cadastro. Então eu tenho que ter um botão localizar cliente. Esse botão irá abrir a tela de pesquisa de cliente (abrir em showmodal) e quando o usuário selecionar o cliente, é retornado para a janela anterior o cliente selecionado.
Eu gostaria de saber o que fazer para não ter que repetir sempre o código de instanciar a janela de pequisa, obter o objeto selecionado. Teria alguma coisa?
GOSTEI 0
Claudio Silva
09/04/2009
Olá pessoal! Oi Ronaldo.
Para tentar afiar mais suas habilidades em swing e ajudar a atender à demanda do Ronaldo, neste tutorial vamos estender as capacidades do nosso MeuNote com duas novidades: a construção de comandos reutilizáveis, para botões de barra de ferramentas e menu. No próximo artigo vamos expandir nossa aplicação para usar MDI.
Como criar comandos reutilizáveis?
Uma grande evolução na forma como tratamos os comandos de usuário é encapsular os procedimentos a serem executados em objetos que possuem uma interface comum. Assim poderemos tratar todos os comandos do usuário da mesma forma. Um usuário aperta um botão de salvar e um arquivo é salvo, um usuário aperta um botão e um arquivo é aberto... A forma de acionar a ação é comum (apertar um botão), mas o procedimento executado é diferente. Um padrão de projeto que resolve este tipo de problema é o chamado padrão [b]Command [/b](pesquise por Command - Design Patterns - GoF). O Swing implementa esse padrão através da interface [color=red:9a7cf41c63][b]Action[/b][/color:9a7cf41c63] e da classe [color=orange:9a7cf41c63][b]AbstractAction[/b][/color:9a7cf41c63].
Para mostrar em termos simples como a coisa funciona, vamos dotar nosso notepad com uma barra de ferramentas, uma barra de menus e alguns comandos básico de novo, abrir e salvar textos. Primeiramente, vamos construir o novo esqueleto da janela:
A principal coisa a notarmos nesse código é referente aos menus e a barra de ferramentas. Passamos a eles objetos das classes NovoAction, SalvarAction e AbrirAction. Notamos também que essas classes possuem a interface [b]Action [/b]([i]javax.swing.Action[/i]) em comum. Que tipo de objeto é esse?
Em swing, um objeto Action encapsula todo o necessário para gerar um botão ou item de menu. O objeto sabe informar (a quem perguntar) o nome da ação que executa, a imagem de seu botão, a descrição curta ou extensa sobre o trabalho que realiza e, por fim, ele sabe realizar a tarefa para o qual foi desenhado. Tudo isso em um só objeto (ou seja, ele é mais do que um simples ouvinte de evento)!
Como criar este tipo de objeto? Vou mostrar aqui como escrever as classes NovoAction e SalvarAction, deixando a última, AbrirAction, como exercício pra você, ok? Bem, uma forma bem mais fácil de escrever uma action é criando uma subclasse de [b][i]java.swing.AbstractAction[/i][/b]:
A parte interessante disso tudo é que, se tivermos outro projeto em que seja preciso apagar, salvar ou abrir arquivos de texto em uma JTextArea, poderemos reaproveitar as classes de ação facilmente, pois elas já são razoavelmente auto-suficientes e são pouco dependentes do MeuNote em si (um desafio para você: como aumentar ainda mais o potencial de reutilização dessas actions, de forma que elas não dependam mais da referência a uma JTextArea, mas possam apagar, salvar e abrir arquivos em qualquer lugar?).
Quando construir seu projeto, verá que as ações se tornam menus e botões "automagicamente". Quando um menu é clicado, ou um botão da barra de ferramentas é criado, o método actionPerformed da ação correspondente á invocado.
Por hora terminamos esse tutorial. Por questão de brevidade, não respondi todas as duvidas possíveis, embora saiba que muitas serão levantadas. Fico no aguardo de vocês para fazerem as perguntas.
Esse tutorial vai facilitar o entendimento do próximo artigo, onde vou mostrar a construção de interfaces MDI. Até lá! :!:
import java.wat.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
//Procure no javadoc cada umas das classes que vc. não conhece neste exemplo. Assim vc. saberá em que pacotes elas estão e as conhecerá melhor! ;-)
public class MeuNote extends JFrame{
//Componentes
private JToolBar toolbar = new JToolBar("Ferramentas");
private JMenuBar menubar = new JMenuBar();
private JMenu arquivo = new JMenu("Arquivo");
private JTextArea texto = new JTextArea();
//Ações:
private Action novo = new NovoAction(this.texto);
private Action salvar = new SalvarAction(this.texto);
private Action abrir = new AbrirAction();
public MeuNote(){
super("Meu Notepad");
//Desliga automaticamente a aplicação quando o usuário fecha a janela.
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container interno = this.getContentPane();
this.montaMenu();
this.montaToolBar();
this.montaGUI();
}
private void montaMenu(){
//JMenuItem pode ser construído a partir de um objeto que implementa a interface Action
JMenuItem itemNovo = new JMenuItem(this.novo);
JMenuItem itemSalvar = new JMenuItem(this.salvar);
JMenuItem itemAbrir = new JMenuItem(this.abrir);
this.arquivo.add(itemNovo);
this.arquivo.add(itemSalvar);
this.arquivo.add(itemAbrir);
this.menubar.add(this.arquivo);
this.setJMenuBar(this.menubar);
}
private void montaToolBar(){
//Barras de ferramenta swing também aceitam objetos que implementam Action como parâmetro de construtor
this.toolbar.add(this.novo);
this.toolbar.add(this.salvar);
this.toolbar.add(this.abrir);
}
private void montaGUI(Container interno){
interno.setLayout(new BorderLayout());
interno.add(this.toolbar, BorderLayout.NORTH);
interno.add(new JScrollPane(this.texto));
}
public static void main(String args[]){
//Vc. sabe o que fazer...
}
}
public class NovoAction extends AbstractAction{
private JTextArea texto;
public NovoAction(JTextArea texto){
//Define o nome da ação
super("Novo");
//Define mais algumas características
this.putValue(Action.SMALL_ICON, new ImageIcon("new.gif"));
this.putValue(Action.SHORT_DESCRIPTION, "Limpa a área de texto");
//Consultem a documentação de javax.swing.Action para outras propriedades
}
//Definimos aqui o procedimento que será executado quando NovoAction for acionado
public void actionPerformed(ActionEvent ev){
this.texto.setText("");
}
}public class SalvarAction extends AbstractAction{
private JTextArea texto;
public SalvarAction(JTextArea texto){
//Define o nome da ação
super("Salvar");
//Define mais algumas características
this.putValue(Action.SMALL_ICON, new ImageIcon("save.gif"));
this.putValue(Action.SHORT_DESCRIPTION, "Salva arquivo texto");
//Consultem a documentação de javax.swing.Action para outras propriedades
}
//Definimos aqui o procedimento que será executado quando NovoAction for acionado
public void actionPerformed(ActionEvent ev){
JFileChooser jfc = new JFileChooser();
int resp = jfc.showSaveDialog(this.texto);
if(resp != JFileChooser.APPROVE_OPTION) return;
File arquivo = jfc.getSelectedFile();
this.saveFile(arquivo);
}
//Aqui trabalhamos com classes do java.io
private void saveFile(File f){
try{
FileWriter out = new FileWriter(f);
out.write(this.texto.getText());
out.close();
}catch(IOException e){
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
}GOSTEI 0
Dalton
09/04/2009
Parabéns pelos tutoriais, estou adorando (eu que não conhecia muito de swing :) )
Apenas duas correções:
É obrigatório o tratamento para a IOException, e o
File arquivo = jf[b]b[/b]c.getSelectedFile(); não existe, é jfc, mas é apenas um detalhe! :o
Ahh, na MeuNote.java, no método:
A classe JScrollBar está acusando um erro:
Na invocação do método montaGUI, você deverá passar o interno como parâmetro também :!:
private void saveFile(File f) {
FileWriter out = new FileWriter(f);
out.write(this.texto.getText());
out.close();
} private void montaGUI(Container interno){
interno.setLayout(new BorderLayout());
interno.add(this.toolbar, BorderLayout.NORTH);
interno.add(new JScrollBar(this.texto));
} The constructor JScrollBar(JTextArea) is undefined
:arrow:
[b]Editado novamente:[/b]
public MeuNote(){
super("Meu Notepad");
//Desliga automaticamente a aplicação quando o usuário fecha a janela.
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container interno = this.getContentPane();
this.montaMenu();
this.montaToolBar();
this.montaGUI();
} GOSTEI 0
Claudio Silva
09/04/2009
Obrigado pelas correções... Já corrigi o que eu vi . É que eu não tive muito tempo pra escrever a segunda parte e tive que escrever o código de cabeça... sorry! :oops:
GOSTEI 0
Junior.esnaola
09/04/2009
muito legal o tutorial Copérnico :!:
só a JScrollBar esta desalinhada, fiz uns testes e consegui arruma-la:
interno.add(new JScrollBar(), BorderLayout.EAST); :shock:
me puxei pois nunca tinha feito nada com swing :shock:
mas nao consegui fazer a classe abre :cry:
se alguem fez e quizer posta-la ae pra mim dar uma olhada basica agradeço :D
GOSTEI 0
Claudio Silva
09/04/2009
Eu errei, era JScrollPane, e não JScrollBar...
Já que muita gente se interessou, eu vou ver se consigo um tempo pra escrever direito, compilar tudo e por no download... :!:
GOSTEI 0
Claudio Silva
09/04/2009
Visitante.
Sua dúvida diz respeito à exibição e atualização de dados em um formulário, certo? Neste tutorial, ainda não chegamos lá ainda, estaremos tratando de outro escopo.
Sendo assim, resolvi mover a sua dúvida para outro tópico, onde poderemos escrever especificamente sobre este assunto, ok? :!:
Nos encontramos em: [url]http://www.javafree.com.br/forum/viewtopic.php?t=13989[/url] :!:
GOSTEI 0
Lich King
09/04/2009
há alguma diferenca d performance entre usar JInternalFrame ou JFrame???
qual dos dois eh + aconselhável usar???
GOSTEI 0
Claudio Silva
09/04/2009
São usados para propósitos diferentes...
JFrame, assim como JWindow e JDialog (e suas contrapartes AWT) é um container "top-level", ou seja, pode ser desenhado diretamente no contexto gráfico do sistema operacional.
JInternalFrame é uma janela que só pode ser adicionada dentro de um JDesktopPane. Ela não pode ser desenhada sem ele e é feita especialmente para isso (vc. não pode pôr um JFrame dentro de um JDesktopPane). Para criar uma interface MDI vc. usa um JFrame, um JDesktopPane dentro dele e 1..n JInternalFrames, que aparecerão dentro do JDesktopPane.
Como vc. pode ver JFrame e JInternalFrame tem usos inteiramente diferentes, um não pode substituir o outro...
GOSTEI 0
Lich King
09/04/2009
esses action funcionam com textfields???? :arrow:
GOSTEI 0
Bugaavila
09/04/2009
Salve salve Javaneses.
Preliminarmete registro meus agradecimentos pelo tutorial.
Apenas alguns ajustes que tive que fazer em meu projeto.
Ao clicar nas opções de menu para fazer chamada aos objetos actions, o compilador retornava erro de "NullPointerException". Isso ocorre pq a referencia ao objeto na classe action sempre referenciava a instancia texto desta propria classe (this.texto....), assim a superclasse não conseguia "enxergar" tal objeto.
Solução:
Passei como parâmetro para meu construtor das classes actions o meu JTextArea (texto) da superclasse, e em seguida a subclasse.texto recebeu meu objeto superclasse.texto. Veja no código:
Existem outras maneiras de resolver este problema, como por exemplo austar a referência ao objeto texto da seguinte maneira. [i]NomeDaSuperClasse[/i].this.objeto. Talvez eta não seja a forma mais elegante de resolver o problema, pois você poderá ter problemas de reutilização de código.
Por fim, estou submetendo minha classe [i]AbrirAction[/i]:
Até a próxima!
Adriano Ávila
public class NovoAction extends AbstractAction{
private JTextArea texto;
public NovoAction(JTextArea txt){
super("Novo");
this.putValue(Action.SMALL_ICON, new ImageIcon("new.gif"));
this.putValue(Action.SHORT_DESCRIPTION, "Limpa a área de texto");
/*Aqui objeto de instancia texto recebe o objeto
* da superclasse texto.
*/
texto = txt;
}
...
...
...
package br.est.aca.df.projectSWING;
import java.awt.event.ActionEvent;
import java.io.*;
import java.io.IOException;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JTextArea;
public class AbrirAction extends AbstractAction{
private JTextArea texto;
private File file;
public AbrirAction(JTextArea txt){
//Define o nome da ação
super("Abrir");
//Define mais algumas características
this.getValue("Abrir");
this.putValue(Action.SMALL_ICON, new ImageIcon("new.gif"));
this.putValue(Action.SHORT_DESCRIPTION, "Abre arquivo para edição");
texto = txt;
}
public void actionPerformed(ActionEvent ev){
//private String err="";
try{
JFileChooser jfc = new JFileChooser();
jfc.setFileSelectionMode(jfc.FILES_ONLY);
int resp = jfc.showOpenDialog(this.texto);
if(resp== jfc.CANCEL_OPTION){
return;
}else{
file = jfc.getSelectedFile();
openFile(file);
}
}
catch(Exception e){
JOptionPane.showMessageDialog(null,e.getMessage(),"Erro na abertura de aquivo",JOptionPane.ERROR_MESSAGE);
}
}
private void openFile(File f){
try{
FileReader rd = new FileReader(f);
int i = rd.read();
String ret="";
while(i!=-1){
ret = ret+(char)i;
i = rd.read();
}
this.texto.setText(ret);
}catch(IOException e){
JOptionPane.showMessageDialog(null, e.getMessage());
}
}
}
GOSTEI 0
Lich King
09/04/2009
como q usa a herança d formulários no java???? :arrow:
GOSTEI 0
Ronaldo Luiz
09/04/2009
Da mesma forma que usa herança de qualquer outro objeto. Basta extender a classe usando o comando extends.
public class FormularioFilho extends FormularioPai {...}
GOSTEI 0
Lich King
09/04/2009
mas como q eu faco isso usando netbeans???
GOSTEI 0
Ronaldo Luiz
09/04/2009
ai já nao sei.
mas normalmente é só mandar criar uma classe qualquer e digitar o código fonte da classe.
GOSTEI 0
Leod
09/04/2009
Como eu colocaria, digamos o Nome do arquivo aberto no Titulo (setTitle(String) ), e como quando eu fosse Slavar o arquivo ja aberto, existente, ja salvava direto ser ter de escolher o nome, e o local?
Pq extendendo a classe não consegui setar o Titulo depois, mas criando um Objeto JFrame, e fazendo torcando o this pelo objeto criado eu consegui.Teria algum outro jeito?
Muito Obrigado
GOSTEI 0
Hero_of_sand
09/04/2009
eu to com um problema.... espero que vcs possam me responder o mais rapido possivel....
eu ta querendo salvar arquivos apenas .txt, para isso to tentando forçar a extensao .txt (se o arquivo nao for .txt ou .TXT). Para isso to tentando renomear o arquivo que o usuario vai salvar mas nunca da certo pq o metodo renameTo sempre ta retornando false... num sei pq..
to fazendo assim..
sendo que file é o arquivo que é o File que recebe o getSelectedFile() do JFileChooser....
private void verificaTXT(File file){
String nmarq = file.getName();
int tamstr = nmarq.length();
if(tamstr>=4){
String extensao = nmarq.substring(tamstr - 4, tamstr);
if(!((extensao.equals(".txt"))||(extensao.equals(".TXT")))){
File file2 = new File(nmarq+".txt");
boolean success = file.renameTo(file2);
}
}else{
String nmarq2 = nmarq + ".txt";
File file2 = new File(nmarq2);
file.renameTo(new File(nmarq2));
}
}
GOSTEI 0
Carlos Heuberger
09/04/2009
A documentacao diz que o metodo pode falhar:
1) por nao conseguir mover o arquivo de um disco (filesystem) para outro
2) se ja existir um arquivo com esse nome/caminho
e talvez tambem pode dar problema se o arquivo estiver 'aberto' em outra aplicacao ou pelo proprio programa
Meu conselho: faz um system.out.println do arquivo velho e do novo, e tambem verifique a (nao)existencia do novo arquivo, para ter certeza do que esta acontecendo
[]]]
System.out.println(file + ", file:" + file.isFile() + " read:" + file.canRead()); System.out.println(file2 + ", file:" + file2.isFile() + " exists:" + file2.exists());
GOSTEI 0
Paulo Lopes
09/04/2009
podia fazer um breve tuto sobre icon tray.
GOSTEI 0
Simone Silva
09/04/2009
this.montaGUI(); ocorre o seguinte erro nesta linha
cannot be applied to()
nao estou conseguindo resolver
GOSTEI 0