Construção de componentes: JavaBeans

Introdução a JavaBeans

Conceitos básicos de JavaBeans

Propriedades

public Color getCor();
public void setCor(Color cor);

beans1.gif (2794 bytes)

public String[] getEstados()
public String getEstados(int índice)
public void setEstados(String[] estados)
public void setEstados(int índice, String estados)

beans2.gif (4535 bytes)

beans3.gif (7370 bytes)

beans4.gif (8534 bytes)

Eventos

public void addPropertyChangeListener(PropertyChangeListener pcl);
public void removePropertyChangeListener(PropertyChangeListener pcl);
public void addVetoableChangeListener(VetoableChangeListener pcl);
public void removeVetoableChangeListener(VetoableChangeListener pcl);
public void add<Custom>Listener(<Custom>Listener cl);
public void remove<Custom>Listener(<Custom>Listener cl);

Um Bean mínimo

Classes especiais para ajudar

Packaging de JavaBeans

Manifest-Version: 1.0
Name: tv/BotaoOnOff.class
Java-Bean: True
Created-By: 1.2 (Sun Microsystems Inc.)

Name: tv/Limites.class
Java-Bean: True

Name: tv/TVDisplay.class
Java-Bean: True

Name: tv/KeyPad.class
Java-Bean: True

Name: tv/TVEstado.class
Java-Bean: True

Uma Demonstração

tv1.gif (5420 bytes)

O design e código do exemplo Demo

Elementos básicos

Design de Beans úteis para resolver o problema

O Bean BotaoOnOff

tv2.gif (3895 bytes)

package tv; // poderia estar num package mais genérico

import java.awt.*;
import java.beans.*;
import java.io.*;

/**
 * Um botão que acende quando está "on"
 */
public class BotaoOnOff extends Button
        implements Serializable, PropertyChangeListener {
    protected boolean   on; // estado do botão
    protected PropertyChangeSupport pcs;

    // Construtor
    public BotaoOnOff() {
        // Cada bean tem um construtor default ("no-arg")
        super("Liga/Desliga");
        pcs = new PropertyChangeSupport(this);
        on = false;
    }

    // Método getter para propriedade "on"
    public boolean isOn() {
        return on;
    }

    // Método setter para propriedade "on"
    public void setOn(boolean on) {
        // Dispara a mudança para os listeners, se houver
        pcs.firePropertyChange("on", new Boolean(this.on),
                               new Boolean(on));
        this.on = on;
        mudaVisual();
    }

    // Muda o visual da "lâmpada on/off"
    private void mudaVisual() {
        setBackground(isOn() ? Color.red : Color.gray);
    }

    // Se este bean for listener da propriedade "on"
    // de outro bean, então precisamos deste método
    public void propertyChange(PropertyChangeEvent pce) {
        String propriedadeMudada = pce.getPropertyName();
        Object novoValor = pce.getNewValue();
        if(propriedadeMudada.equals("on")) {
            this.setOn(((Boolean)novoValor).booleanValue());
        }
    }

    // Método de cadastro de listeners
    // usa delegação para implementar
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }

    // Método de remoção de listeners
    // usa delegação para implementar
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }
}

O botão comum

tv3.gif (3053 bytes)

O bean KeyPad

tv4.gif (3263 bytes)

package tv;

import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;

/**
 * Um keypad
 */
public class KeyPad extends Container implements ActionListener {
    protected int   valor = 0;      // propriedade do bean
    protected int   maxDígitos = 3; // propriedade do bean:
                                    // número max de dígitos
    private String  valorDisplay;   // valor mostrado no display
                                    // antes de dar ENTER
    protected PropertyChangeSupport pcs;
    private Button botãoEnter = new Button("ENTER");
    private Label display = new Label("");

    // Construtor
    public KeyPad() {
        pcs = new PropertyChangeSupport(this);
        Panel p = new Panel(); // para os botões de 0 a 9
        p.setLayout(new GridLayout(2,5));
        setLayout(new BorderLayout()); // para o keypad inteiro

        // cria os botões
        for(int i = 0; i < 10; i++) {
            Button b = new Button(Integer.toString(i));
            p.add(b);
            b.addActionListener(this);
        }
        botãoEnter.addActionListener(this);
        add("South", botãoEnter);
        add("Center", p);
        add("North", display);
        display.setAlignment(Label.CENTER);
        valorDisplay = "";
    }

    // getter do valor
    public int getValor() {
        return valor;
    }

    // setter do valor
    public void setValor(int valor) {
        pcs.firePropertyChange("valor", new Integer(this.valor),
                                        new Integer(valor));
        this.valor = valor;
        valorDisplay = "";
        display.setText(valorDisplay);
    }

    // getter de maxDígitos
    public int getMaxDígitos() {
        return maxDígitos;
    }

    // setter de maxDígitos
    public void setMaxDígitos(int maxDígitos) {
        this.maxDígitos = maxDígitos;
    }

    // Mudança do display
    public void mudaDisplay(String digito) {
        valorDisplay += digito;
        // só permite maxDígitos digitos de display
        if(valorDisplay.length() > maxDígitos) {
            valorDisplay = digito;
        }
        display.setText(valorDisplay);
    }

    // cadastro para mudança de propriedade
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }

    // remoção de cadastro para mudança de propriedade
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }

    // trata o evento de clique de qualquer botão
    public void actionPerformed(ActionEvent ae) {
        if(ae.getSource().equals(botãoEnter)) {
            try {
                setValor(Integer.parseInt(valorDisplay));
            } catch(NumberFormatException nfe) {
                // "não deveria" acontecer
                nfe.printStackTrace();
                return;
            }
        } else {
            mudaDisplay(ae.getActionCommand());
        }
    }
}

tv5.gif (6287 bytes)

O Bean Limites

package tv;

import java.beans.*;
import java.io.*;

/**
 * Verifica se um valor inteiro cai entre dois limites
 * e lança exceção se não aceitável
 */
public class Limites
        implements VetoableChangeListener, Serializable {

    protected int inferior; // propriedade: limite inf. para "valor"
    protected int superior; // propriedade: limite sup. para "valor"
    protected int valor;    // propriedade: o valor do Bean
    protected PropertyChangeSupport pcs;

    public Limites() {
        inferior = 1;   // valores default
        superior = 99;
    }

    // getter de inferior
    public int getInferior() {
        return inferior;
    }

    // setter de inferior
    public void setInferior(int inferior) {
        this.inferior = inferior;
    }

    // getter de superior
    public int getSuperior() {
        return superior;
    }

    // setter de superior
    public void setSuperior(int superior) {
        this.superior = superior;
    }

    // verifica validade do valor
    public boolean isValorValido(int valor) {
        return inferior <= valor && valor <= superior;
    }

    public void setValor(int valor) {
        pcs.firePropertyChange("valor", new Integer(this.valor),
                               new Integer(valor));
        this.valor = valor;
    }

    // vetoableChange - é aqui que recebemos
    // o evento de mudança de valor
    // e verificamos se é aceitável
    public void vetoableChange(PropertyChangeEvent pce)
            throws PropertyVetoException {

        String propriedadeMudada = pce.getPropertyName();
        Object novoValor = pce.getNewValue();
        if(propriedadeMudada.equals("valor")) {
            int valorProposto = ((Integer)novoValor).intValue();
            if(isValorValido(valorProposto)) {
                setValor(valorProposto);
            } else {
                throw new PropertyVetoException(
                           "Valor fora dos limites", pce);
            }
        }
    }
}

O Bean TVDisplay

package tv;

import java.awt.*;
import java.beans.*;
import java.io.*;

/**
 * Um display de TV simples
 */
public class TVDisplay extends Canvas
        implements PropertyChangeListener, Serializable {
    protected int       canal;  // propriedade
    protected boolean   on;     // propriedade

    protected PropertyChangeSupport pcs;
    private Color corCorrente; // representa on/off

    // Construtor
    public TVDisplay() {
        super();
        pcs = new PropertyChangeSupport(this);
        canal = 0;
        on = false;
        corCorrente = Color.gray;
    }

    public void paint(Graphics g) {
        int altura = getSize().height;
        int largura = getSize().width;
        int x = 0;
        int y = 0;
        int offset = 1;
        g.setColor(Color.black);
        g.drawRect(x, y, largura-offset, altura-offset);
        g.setColor(corCorrente);
        g.fillRect(x+1, y+1, largura-offset-1, altura-offset-1);
        g.setColor(Color.red);
        g.drawString(String.valueOf(canal), largura/2, altura/2);
    }

    // getter para "on"
    public boolean isOn() {
        return on;
    }

    // setter para "on"
    public void setOn(boolean on) {
        if(on != this.on) {
            pcs.firePropertyChange("on", new Boolean(this.on),
                                         new Boolean(on));
            this.on = on;
        }
        corCorrente = isOn() ? Color.white : Color.gray;
        repaint();
    }

    // getter para o canal
    public int getCanal() {
        return canal;
    }

    // setter para canal
    public void setCanal(int canal) {
        pcs.firePropertyChange("canal", new Integer(this.canal),
                                        new Integer(canal));
        this.canal = canal;
        repaint();
    }

    // trata da mudança da propriedade canal de outro bean
    public void propertyChange(PropertyChangeEvent pce) {
        String propriedadeMudada = pce.getPropertyName();
        Object novoValor = pce.getNewValue();
        // mudança de "valor" de outro Bean altera o canal
        if(propriedadeMudada.equals("valor")) {
            this.setCanal(((Integer)novoValor).intValue());
        }
        if(propriedadeMudada.equals("on")) {
            this.setOn(((Boolean)novoValor).booleanValue());
        }
    }

    // cadastro para mudança de propriedade
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }

    // remoção de cadastro para mudança de propriedade
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }
}

O Bean TVEstado

tv6.gif (12305 bytes)

package tv;

import java.beans.*;
import java.io.*;

/**
 * Manutenção do estado da TV
 */
public class TVEstado implements Serializable {
    protected int       canal;  // propriedade: canal da TV
    protected boolean   on;     // propriedade: TV ligada ou não
    protected PropertyChangeSupport pcs;
    protected VetoableChangeSupport vcs;

    // Construtor
    public TVEstado() {
        pcs = new PropertyChangeSupport(this);
        vcs = new VetoableChangeSupport(this);
        canal = 0;
        on = false;
    }

    // getter para propriedade "on"
    public boolean isOn() {
        return on;
    }

    // setter para propriedade "on"
    public void setOn(boolean on) {
        if(on != this.on) {
            pcs.firePropertyChange("on", new Boolean(this.on),
                                   new Boolean(on));
            this.on = on;
        }
    }

    // getter para propriedade "canal"
    public int getCanal() {
        return canal;
    }

    // setter para propriedade "canal"
    public void setCanal(int canal) throws PropertyVetoException {
        vcs.fireVetoableChange("valor", new Integer(this.canal),
                               new Integer(canal));
        // se for vetado, não chegaremos aqui
        this.canal = canal;
    }

    // Incrementa canal
    public void incrementaCanal() {
        mudaCanal(1);
    }

    // Decrementa canal
    public void decrementaCanal() {
        mudaCanal(-1);
    }

    private void mudaCanal(int incremento) {
        try {
            setCanal(getCanal() + incremento);
        } catch(PropertyVetoException e) {
            // mensagem de visualização para testes
            System.out.println("Vetado!");
        }
    }

    // Método de mudança on/off
    public void onOff() {
        setOn(!isOn());
    }

    // cadastro para mudança de propriedade
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }

    // remoção de cadastro para mudança de propriedade
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }

    // cadastro para mudança "vetável" de propriedade
    public void addVetoableChangeListener(VetoableChangeListener l) {
        vcs.addVetoableChangeListener(l);
    }

    // remoção de cadastro para mudança "vetável" de propriedade
    public void removeVetoableChangeListener(VetoableChangeListener l) {
        vcs.removeVetoableChangeListener(l);
    }
}

Como o BDK "cola" os Beans

jar cfm tv.jar manifest.txt tv/BotaoOnOff.class
        tv/TVEstado.class tv/Limites.class
        tv/KeyPad.class tv/TVDisplay.class
// Automatically generated event hookup file.

package tmp.sunw.beanbox;
import tv.TVEstado;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

public class ___Hookup_15e5be4184
        implements java.awt.event.ActionListener,
                   java.io.Serializable {

    public void setTarget(tv.TVEstado t) {
        target = t;
    }

    public void actionPerformed(java.awt.event.ActionEvent arg0) {
        target.onOff();
    }

    private tv.TVEstado target;
}

tv7.gif (4235 bytes)

Construindo a aplicação

  1. Arraste um BotaoOnOff para o controle remoto e altere o label se desejar
  2. Arraste dois ourButton para o controle remoto e altere os labels para Up e Down
  3. Arraste um KeyPad para o controle remoto e altere o número de dígitos se desejar
  4. Arraste um BotaoOnOff para a TV e altere o label se desejar
  5. Arraste um TVEstado, TVDisplay e Limites para a TV
  6. Altere os limites inferior e superior de Limites se desejar
  1. Liga/Desliga Remoto/ActionPerformed para TVEstado/OnOff
  2. Liga/Desliga TV/ActionPerformed para TVEstado/OnOff
  3. TVEstado/BindProperty/on para Liga/desliga Remoto/on
  4. TVEstado/BindProperty/on para Liga/desliga TV/on
  5. TVEstado/BindProperty/on para TVDisplay/on
  6. Up/ActionPerformed para TVEstado/incrementaCanal
  7. Down/ActionPerformed para TVEstado/decrementaCanal
  8. Keypad/BindProperty/valor para TVEstado/canal
  9. TVEstado/Event/VetoableChange para Limites/vetoableChange
  10. Limites/PropertyChange para TVDisplay/PropertyChange
appletviewer tvDemo.html

Features Avançados de JavaBeans

Property Editors

Uso da classe BeanInfo

Customizadores

Bridge ActiveX

comp-3 programa anterior próxima