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 (3131 bytes)

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

beans2.gif (5130 bytes)

beans3.gif (9364 bytes)

beans4.gif (10429 bytes)

Eventos

O modelo de eventos no JavaBeans

public void addPropertyChangeListener(
                      PropertyChangeListener pcl);
public void removePropertyChangeListener(
                      PropertyChangeListener pcl);
public void addVetoableChangeListener(
                      VetoableChangeListener vcl);
public void removeVetoableChangeListener(
                      VetoableChangeListener vcl);
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 (4561 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 {

  private boolean   on; // estado do botão
  private 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,
  // entao 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 (3734 bytes)

O bean KeyPad

tv4.gif (3744 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 {
  private int   valor = 0;      // propriedade do bean
  private int   maxDígitos = 3; // propriedade: número max de dígitos
  private String  valorDisplay;   // valor do display antes de dar ENTER
  private 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 conter 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 (7344 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 {

  private int inferior; // propriedade: limite inferior para "valor"
  private int superior; // propriedade: limite inferior para "valor"
  private int valor;    // propriedade: o valor do Bean
  private PropertyChangeSupport pcs;

  public Limites() {
    pcs = new PropertyChangeSupport(this);
    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;
  }

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

  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);
      }
    }
  }

  // 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 TVDisplay

package tv;

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

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

  private PropertyChangeSupport pcs;

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

  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(corDaTela());
    g.fillRect(x+1, y+1, largura-offset-1, altura-offset-1);
    if(isOn()) {
      g.setColor(Color.green);
      g.drawString(String.valueOf(canal), largura/2, altura/2);
    }
  }

  private Color corDaTela() {
    // cor da tela depende do canal sintonizado
    Random nr = new Random(canal);
    return isOn() ? new Color(nr.nextInt(256), 
                              nr.nextInt(256), 
                              nr.nextInt(256)) 
                  : Color.gray;
  }

  // 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;
    }
    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 (15332 bytes)

package tv;

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

/**
 * Manutenção do estado da TV
 */
public class TVEstado implements Serializable {
  private int       canal;  // propriedade: canal da TV
  private boolean   on;     // propriedade: TV ligada ou não
  private PropertyChangeSupport pcs;
  private 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

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
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 (8618 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 (gera hookup)
  2. Liga/Desliga TV/ActionPerformed para TVEstado/OnOff (gera hookup)
  3. TVEstado/BindProperty/on para Liga/desliga Remoto/on
    (o BDK uso um hookup interno da classe PropertyHookup)
  4. TVEstado/BindProperty/on para Liga/desliga TV/on
  5. TVEstado/BindProperty/on para TVDisplay/on
  6. Up/ActionPerformed para TVEstado/incrementaCanal (gera hookup)
  7. Down/ActionPerformed para TVEstado/decrementaCanal (gera hookup)
  8. Keypad/BindProperty/valor para TVEstado/canal
  9. TVEstado/Event/VetoableChange para Limites/vetoableChange (gera hookup)
  10. Limites/BindProperty/valor para TVDisplay/canal
appletviewer tvDemoApplet.html

Features Avançados de JavaBeans

Property Editors

Uso da classe BeanInfo

Customizadores

Bridge ActiveX

comp-3 programa anterior próxima