Java: GUI, Events, Listeners, Handlers e programação gráfica

Aprenda a usar Events, Listeners, Handlers, JFrame, JButton, JLabel e JTextField criando um jogo em GUI (interface gráfica) do início ao fim em Java!

ActionListener, ActionEvent, Handlers...essas coisas só se aprende na prática!
Nada melhor que criar seu jogo para aprender Java.


Uma aplicação simples usando JFrame, JButton, JLabel e JTextField


Quando se pensa em programar, logo se pensa em criar uma nova versão de Quake III - Arena ou um novo sistema operacional que não trave e seja super seguro.

Porém, com o passar do tempo, vemos que estamos usando somente a linha de comando do Shell ( ou do DOS, caso você ainda não tenha visto a luz), e sua namorada não aguenta mais testar aqueles joguinhos naquela maldita tela preta.

Uma das vantagens do Java é a facilidade (?) de criar aplicativos com interface gráfica. 
Pra ilustrar o uso desses elementos gráficos, vou mostrar como criar esse simples jogo, que gera um número e você tem que adivinhar o número através das dicas.







Então vamos lá, começando por alguns dos principais elementos:

  • JFrame
Frame é uma uma moldura, ou uma janela. Sabe aquela janelinha do programa? É um frame.
Sabe quando clica em algo, geralmente um botão, e abre outra janelinha? Aquela outra janela é outro frame.
Cada uma delas é representada por uma classe. Se uma janela surge dentro de outra, tem a ver com herança. Se uma janela pode se apresentar de várias formas, mas com a mesma base de outras, tem a ver com polimofirsmo (note a importância das classes).

  • JLabel
Label é simplesmente um texto, ou rótulo, ao pé da letra.

  • JTextField
É o campo de texto (sabendo inglês, muitas coisas são óbvias) onde você vai escrever. É onde você vai enviar string (texto) pro programa, é uma espécia de canal de comunicação entre o usuário e o programa.



Com base nesses simples elementos, iremos criar um joguinho (nada de 'hello world', você pode até enviar pros seus amigos).
O programa gera um número, entre 1 e 1000, aleatoriamente, e você tem que adivinhar que número é esse. Se o seu palpite for maior ou menor que o número gerado, o programa avisa isso, até você acertar o dito cujo.


Criando o Frame principal

Vamos usar somente um frame nesse aplicativo, um Frame simples, com um texto (label), um campo pra você introduzir o número, um botão pra testar se o seu palpite está certo e outro pra gerar um número aleatório, para jogar novamente.

Vamos chamar nosso frame de window:

public class window extends JFrame
{
}
Pra usar a JFrame precisamos adicionar:
import javax.swing.JFrame;

Tudo que queremos exista nesse frame, colocamos dentro dessa classe.


    private JButton generateButton; // gerar outro número

    private JButton guessButton;     // testar seu palpite

    private JTextField textField;     // campo de texto

    private JLabel guess;               // texto "Digite um numero, de 1 até 1000"

    private int number, attemp;    // number é o número gerado aleatoriamente, attemp é o nosso palpite



Pra usar esses elementos (JComponents), precisamos importar: 
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;

Essas são variáveis globais dentro da nossa classe window.


Método pra gerar um número aleatório:


    public void generate()
    {
        Random gen = new Random();
        number = gen.nextInt(1000) + 1;
    }

Para usar, precisamos importar:
import java.util.Random;


Título do programa

O seguinte código criar o título, que fica na parte de cima do Frame, perto dos botões minimizar, maximizar e fecha:

super("Adivinhe o Número 1.0 - by Emerson, Lake & Palmer");

A próxima linha é simplesmente pra definir o Layout do frame:
setLayout(new FlowLayout());
Pra usar, importe:
import java.awt.FlowLayout;

Depois, gere o número:
generate();

Usando o campo de texto


Já declaramos a JTextField, agora vamos especificar suas características, como o tamanho do campo:



        textField = new JTextField(27);



Criar não significa que vai aparecer, pra aparecer no Frame usamos:

        add(textField);

        
Vamos agora criar um handler, uma classe para lidar com o campo de texto. Isso é, quando você digita e aperta enter, a informação vai para o programa. O handler trata como e quando essa mensagem vai, o programa não tem como adivinhar quando você quer que o número digitado seja enviado.

Criamos o handler assim (mais tarde vamos criar a classe TextFieldHandler):
        TextFieldHandler handler = new TextFieldHandler();

Vamos definir que esse handler é que vai lidar com o JComponent textField:
        textField.addActionListener(handler);

addActionListener : é um comando interno, que, como o nome diz, espera a ação acontecer pra passar a informação pro programa.

Para usar, devemos importar:

import java.awt.event.ActionListener;

import java.awt.event.ActionEvent;

Usando com o botão Testar

Exatamente como fizemos com o JTextField, fazemos com um JButton. Ou seja, precisamos de um handler pro programa saber quando clicamos no botão.
A única diferença aqui é que podemos escrever algo no JButton ("Testar", no caso):


        GuessButton guess = new GuessButton();

        guessButton = new JButton("Testar");

        guessButton.addActionListener(guess);

        add(guessButton);



Usando o botão Gerar número

Handler, que espera o usuário clicar no botão para passar essa informação pro programa:


        GenerateAnotherNumber  generateAnotherNumber = new GenerateAnotherNumber ();

        generateButton = new JButton("Gerar outro número");

        generateButton.addActionListener(generateAnotherNumber);

        add(generateButton);

Handler: TextFieldHandler

Os handlers usam todos a classe ActionListener, que é uma classe geralzona, para o tratamento de ações.

public class TextFieldHandler implements ActionListener
{
}

A ActionListener possui um método chamado ActionPerformed, que recebe um evento como argumento (um enter, um click do mouse etc):

public void actionPerformed (ActionEvent event)
{
}

Aqui, o nosso evento é uma variável chamada 'event'.
Quando ela vem da textField, o seguinte if retorna verdadeiro:

if(event.getSource() == textField)


Quando digitamos algo e apertamos enter, o que foi digitado fica na seguinte string:

event.getActionCommand()


Vamos passar o que foi digitado, que é uma string, pra inteiro:

attemp = Integer.parseInt(event.getActionCommand());


Agora vamos para a lógica super complexa do programa.
Caso o nosso palpite seja maior que o número, aparece uma janela dizendo que o palpite é maior.
Caso seja menor, aparece dizendo que é menor.
Caso não seja maior nem menor...exato, Watson.

A janelinha com o texto é a JOptionPane, que recebe dois argumentos: uma referência ao frame (podemos ter vários frames numa mesma aplicação) e uma string.

Para usa-la, devemos importar:

import javax.swing.JOptionPane;

A lógica, que ficará dentro do método test(int attemp), fica:

                if(attemp < number)
                    JOptionPane.showMessageDialog(window.this,"Errou. o número é maior");
                else if(attemp > number)
                    JOptionPane.showMessageDialog(window.this,"Errou, o número é menor");
                else
                {
                    JOptionPane.showMessageDialog(window.this,"Parabéns, voce acertou o numero!");
                    textField.setEditable(false);
                }


esse textField.setEditable(false) não permite que algo seja digitado, e acontece quado você acerta o número.



Handler: GenerateButton


Também é uma ActionListener e também possui uma função actionPerformed, e dentro desta:

            if(event.getSource() == generateButton)
            {
                generate();
                textField.setEditable(true);
                textField.setText("");
            }

Ou seja, após gerar um número, faz com que o campo de texto seja editável e apaga o que tem escrito lá (colocando uma string nula).

Handler: GuessButton

A diferença desse handler é que ele pega o que tem escrito no campo de texto, através do comando:

stringNumber=textField.getText();

Onde stringNumber é uma String pra armazenar o que tem escrito no campo de texto.
Depois passamos essa string pra inteiro e fazemos a mesma lógica do TextFieldHandler.



Pronto, frame criado.
Mas criar/existe é algo, aparecer é outro.
E se existirem milhares de frames, vão todos aparecer? Não.
O que foi explanado é só a classe window.

Vamos criar a classe principal do jogo, que criar a JFrame window, dizer o tamanho dela, se é visível e o que ocorre quando clicamos em Fechar (se minimiza ou realmente fecha o aplicativo, que é o caso).

Isso é feito, na main, assim:

        window mainWindow = new window();        
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainWindow.setSize(400,150);
        mainWindow.setVisible(true);    

Pra usar a JFrame, importe:
import javax.swing.JFrame;


Arquivos .java


GuessTheNumber.java

import javax.swing.JFrame;

public class GuessTheNumber 
{
    public static void main(String[] args) 
    {
        window mainWindow = new window();        
        mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        mainWindow.setSize(400,150);
        mainWindow.setVisible(true);        
        
    }
}



window.java


import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import java.util.Random;

public class window extends JFrame
{
    private JButton generateButton;
    private JButton guessButton;
    private JTextField textField;
    private JLabel guess;
    private int number, attemp;
    
    public void generate()
    {
        Random gen = new Random();
        number = gen.nextInt(1000) + 1;
    }

    public void test(int attemp)
    {
                if(attemp < number)
                    JOptionPane.showMessageDialog(window.this,"Errou. o número é maior");
                else if(attemp > number)
                    JOptionPane.showMessageDialog(window.this,"Errou, o número é menor");
                else
                {
                    JOptionPane.showMessageDialog(window.this,"Parabéns, voce acertou o numero!");
                    textField.setEditable(false);
                }        
    }

    public window()
    {
        super("Adivinhe o Número 1.0 - Programação Progressiva");
        setLayout(new FlowLayout());
        generate();        
        
        //frase principal, pedindo o numero
        guess = new JLabel();
        guess.setText("Digite um numero, de 1 até 1000");
        add(guess);
        
        //adicionando o campo de texto
        textField = new JTextField(27);
        add(textField);
        
        TextFieldHandler handler = new TextFieldHandler();
        textField.addActionListener(handler);
        
        //handler pro botao guessButton
        GuessButton guess = new GuessButton();
        guessButton = new JButton("Testar");
        guessButton.addActionListener(guess);
        add(guessButton);
        
        //handler pro botao generateButton
        GenerateAnotherNumber  generateAnotherNumber = new GenerateAnotherNumber ();
        generateButton = new JButton("Gerar outro número");
        generateButton.addActionListener(generateAnotherNumber);
        add(generateButton);
        
    }
    
    public class TextFieldHandler implements ActionListener
    {
        public void actionPerformed (ActionEvent event)
        {
            //se o evento for enter
            if(event.getSource() == textField)
            {
                attemp = Integer.parseInt(event.getActionCommand());
                attemp = Integer.parseInt(event.getActionCommand());
                test(attemp);
            }
            
        }
    }
    
    private class GenerateAnotherNumber implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
            if(event.getSource() == generateButton)
            {
                generate();
                textField.setEditable(true);
                textField.setText("");
            }
        }
    }
    
    private class GuessButton implements ActionListener
    {
        public void actionPerformed(ActionEvent event)
        {
           if(event.getSource() == guessButton)
            {
                String stringNumber;
                stringNumber=textField.getText();
                attemp = Integer.parseInt(textField.getText());
                test(attemp);
            } 
        }
            
    }
        
}

ou

A abordagem anterior foi um pouco mais OO, orientada a objetos, com um handler para cada possível evento diferente. Mas note que todos são ActionListener e usam a actionPerformed e criamos variáveis não obrigatórias (stringNumber e attemp).

O código a seguir é mais enxuto. Por ser simples, não há problemas usar ele assim, mas caso fosse um projeto maior, é sempre bom primar pela organização (pois é necessário manter o código, depois).

window.java




import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import java.util.Random;

public class window extends JFrame
{
    private JButton generateButton;
    private JButton guessButton;
    private JTextField textField;
    private JLabel guess;
    private int number;
    
    public void generate()
    {
        Random gen = new Random();
        number = gen.nextInt(1000) + 1;
    }
    
    public void test(int attemp)
    {
                if(attemp < number)
                    JOptionPane.showMessageDialog(window.this,"Errou. o número é maior");
                else if(attemp > number)
                    JOptionPane.showMessageDialog(window.this,"Errou, o número é menor");
                else
                {
                    JOptionPane.showMessageDialog(window.this,"Parabéns, voce acertou o numero!");
                    textField.setEditable(false);
                }        
    }

    public window()
    {
        super("Adivinhe o Número 1.0 - by Emerson, Lake & Palmer");
        setLayout(new FlowLayout());
        generate();        
        
        //frase principal, pedindo o numero
        guess = new JLabel();
        guess.setText("Digite um numero, de 1 até 1000");
        add(guess);
        
        //criando e adicionando os handlers
        Handler handler = new Handler();
        
        textField = new JTextField(27);
        textField.addActionListener(handler);
        add(textField);
        
        guessButton = new JButton("Testar");
        guessButton.addActionListener(handler);
        add(guessButton);
        
        generateButton = new JButton("Gerar outro número");
        generateButton.addActionListener(handler);
        add(generateButton);
        
    }
    
    public class Handler implements ActionListener
    {
        @Override
        public void actionPerformed (ActionEvent event)
        {
            //se o evento for enter
            if(event.getSource() == textField)
                test(  Integer.parseInt( event.getActionCommand() )  );
            
            //se o evento for um click no botoão gerar outro número
            else if(event.getSource() == generateButton)
            {
                generate();
                textField.setEditable(true);
                textField.setText("");
            }
            //se o evento for um click no botão testar
            else if(event.getSource() == guessButton)
                test(  Integer.parseInt( textField.getText() )  );
        }
    }
    
}




Esse artigo foi transferido do projeto Programação Progressiva, para a vertente Java Progressivo:
http://www.javaprogressivo.net/2012/08/java-gui-events-listeners-handlers-e_5699.html

Nenhum comentário: