jueves, 3 de mayo de 2012

JTable con imágenes y texto

9 comentarios
Hola a todos y especialmente a Marmoleria Cañones que es la persona que me pidió esta nueva entrada sobre JTable.

En la sección CONSULTAS ¿NECESITAS AYUDA CON TU CÓDIGO? me pedía si podía hacer un nuevo ejemplo con un TableCellRenderer para poder mostrar una imagen y una descripción en un JTable...

Nada más fácil, el TableCellRenderer por defecto que tiene instalado la clase JTable nos renderiza las imágenes sin necesidad de introducir código nuevo. Por lo tanto solo tenemos que pasar un objeto de la clase ImageIcon como contenido de una fila y se pintará automáticamente, siempre que la clase TableCellRenderer sepa que tipo de objeto debe pintar.
Por lo tanto la clave de esto pasa por decir que tipo de objeto se debe pintar, el modelo de datos por defecto de un JTable es el DefaultTableModel, este modelo hace creer al renderer que todos los objetos que debe pintar son Object, por lo que lo único necesario para poder mostrar una imagen es crear una clase que extienda DefaultTableModel y sobreescribir el método que indica que tipo de objeto contiene cada columna.
En el modelo por defecto tenemos:
public Class<?> getColumnClass(int columnIndex) {
 return Object.class;
    }
Y la modificación que debemos hacer:
 private class MyTableModel extends DefaultTableModel {

     public MyTableModel(Object[][] data, Object[] columnNames) {
         super(data, columnNames);
     }

  @Override
  public Class<?> getColumnClass(int columnIndex) {
                Class<?> clazz = Object.class;
  Object aux = getValueAt(0, columnIndex);
   if (aux != null) {
    clazz = aux.getClass();
   }
   
   return clazz;
  }
  
 }


Es decir, nuestra tabla queda así:
package tableIconoDescripcion;

import java.net.URL;

import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class Table extends JTable {

 public Table() {
  super();
  String [] columnNames = new String[]{ "Nombre", "Icono", "Descripción"};
        Object [][] data      = new Object[][]{
          {"Angel", createImage("icono1.jpg"), "angelarcosheredia@gmail.com"},
          {"Juan", createImage("icono2.jpg"), "juan@gmail.com", false},
          {"Ana", createImage("icono3.jpg"), "ana@hotmail.com", false}
          };
        MyTableModel model = new MyTableModel(data, columnNames);
        this.setRowHeight(100);
        this.setModel(model);
 }

 /**
  * Para recuperar una imagen de un archivo...
  * @param path Ruta de la imagen relativa al proyecto
  * @return una imagen
  */
 public ImageIcon createImage(String path) {
  URL imgURL = getClass().getResource(path);
     if (imgURL != null) {
         return new ImageIcon(imgURL);
     } else {
         System.err.println("Couldn't find file: " + path);
         return null;
     }
 }

 private class MyTableModel extends DefaultTableModel {

     public MyTableModel(Object[][] data, Object[] columnNames) {
         super(data, columnNames);
     }

  @Override
  public Class<?> getColumnClass(int columnIndex) {
   Class<?> clazz = Object.class;
   Object aux = getValueAt(0, columnIndex);
   if (aux != null) {
    clazz = aux.getClass();
   }

   return clazz;
  }

 }

}


Este sería el resultado obtenido:



Y para lanzar la aplicación simplemente ejecutamos este código.
package tableIconoDescripcion;

import javax.swing.JFrame;
import javax.swing.JScrollPane;


public class Main {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Componentes Swing en JTable");

  final Table table = new Table();

  // La añadimos a un scroll para mostrar la cabecera
  JScrollPane scroll = new JScrollPane(table);

  frame.add(scroll);
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(800, 200);
     frame.setVisible(true);
 }

 public static void main(String[] args) {

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }

}

Eso es todo, para cualquier otra aclaración, no dudéis en contactar conmigo.

Un saludo

jueves, 19 de abril de 2012

Validable JTextField, espresiones regulares en Java

1 comentarios
Hola compañeros, en la mayoría de los formularios de nuestras aplicaciones nos vemos en la necesidad de validar las entradas del usuario.

Esto en primer lugar puede parecer una tarea tediosa, sin embargo, vamos a ver como realizarla de una manera fácil.

Expresiones regulares en Java

Para ello nos valdremos del uso de expresiones regulares. Las expresiones regulares son una manera de describir un conjunto de cadenas de texto que comparten características comunes. Se pueden usar para buscar, editar o manipular textos.
El soporte para las expresiones regulares en java lo proporciona la API java.util.regex.
Las clases principales de este paquete que vamos a utilizar son la clase Pattern y la clase Matcher.

La clase Pattern proporciona una versión compilada de una expresión regular, no tiene constructor público por lo que para usarlo deberemos invocar a uno de sus métodos estáticos compile.
Un objeto Matcher se encarga de interpretar el patrón y realiza operaciones buscando coincidencias en una cadena introducida. Al igual que la clase Pattern, no dispone de constructores públicos por lo que para obtener una instancia invocaremos al método matcher en un objeto Pattern.

Un ejemplo del uso básico de estas clases lo presentamos a continuación, las utilizaremos para buscar cadenas que sean exactamente “a”, “b” o “c”.
String regEx = "[abc]";
  Pattern pattern = Pattern.compile(regEx);
  Matcher matcher = pattern.matcher("a");
  if (matcher.matches()) {
   System.out.println("Se encontró la cadena buscada");
  }

Siguiendo con el tema de los formularios en Swing, podemos utilizar expresiones de este tipo para crear un componente de texto que valide la entrada.
Para ello crearemos una clase que hereda de JTextField y que nos permite pasar una expresión regular al constructor para validar la entrada cada vez que se pulse una tecla. Nuestro componente quedaría así:

package validableJTextfield;

import java.awt.Color;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.BorderFactory;
import javax.swing.JTextField;
import javax.swing.border.Border;

public class ValidableTextField extends JTextField {

 private Pattern pattern;

 private Border wrongBorder = BorderFactory.createLineBorder(Color.RED);
 private Border defaultBorder;

 /**
  * Constructor.
  * @param regEx Expresión regular para evaluar
  */
 public ValidableTextField(String regEx) {
  super();
  this.defaultBorder = this.getBorder();
  this.setColumns(15);
  this.pattern = Pattern.compile(regEx);
  this.addKeyListener(new KeyListener() {

   @Override
   public void keyTyped(KeyEvent e) { }

   @Override
   public void keyReleased(KeyEvent e) {
    validateText();
   }

   @Override
   public void keyPressed(KeyEvent e) { }
  });
 }

 private void validateText() {
  Matcher matcher = pattern.matcher(this.getText());
  if (!matcher.matches()) {
   this.setBorder(wrongBorder);
  } else {
   this.setBorder(defaultBorder);
  }
 }

}

Os dejo un ejemplo de la construcción de las expresiones regulares más comunes:

[abc]                     “a”, “b”, o “c” (simple)
[^abc]                   Cualquier carácter excepto “a”, “b”, o “c” (negación)
[a-zA-Z]               De la “a” a la “z”, o de la “A” a la “Z”, ambas inclusive(rango)
[a-d[m-p]]            De la “a” a la “d”, o de la “m” a la “p”: [a-dm-p] (union)
[a-z&&[def]]        “d”, “e”, o “f” (intersección)
[a-z&&[^bc]]       “a” a la “z”, excepto “b” y “c”: [ad-z] (resta)
[a-z&&[^m-p]]     “a” a la “z”, pero no de la “m” a la “p”: [a-lq-z] (resta)

Y algunos atajos.
.                            Cualquier carácter.
\d                          Un dígito: [0-9]
\D                         Dígitos no: [^0-9]
\s                          Un espacio en blanco: [ \t\n\x0B\f\r]
\S                         Cualquiera excepto un espacio en blanco: [^\s]
\w                         Un carácter alfanumérico: [a-zA-Z_0-9]
\W                        Un carácter no de palabra: [^\w]

Veamos algunos ejemplos del uso de estar expresiones en nuestro componente, crearemos 4 ValidableTextField y le asignaremos a cada uno una expresión regular distinta para obtener algo como esto:


package validableJTextfield;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;


public class Main {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Validable JTextField");

  JPanel container = new JPanel();

  JLabel labelLetters = new JLabel("Solo letras");
  ValidableTextField textFieldLetters = new ValidableTextField("[a-zA-Z]*");

  JLabel labelNumbers = new JLabel("Solo números");
  ValidableTextField textFieldNumbers = new ValidableTextField("[0-9]*");

  JLabel labelLetters3 = new JLabel("Tres caracteres alfanuméricos");
  ValidableTextField textFieldLetters3 = new ValidableTextField("[a-zA-Z0-9]{3}");

  JLabel labelLetters35 = new JLabel("Entre 3 y 5 caracteres alfanuméricos");
  ValidableTextField textFieldLetters35 = new ValidableTextField("[a-zA-Z0-9]{3,5}");

  container.add(labelLetters);
  container.add(textFieldLetters);

  container.add(labelNumbers);
  container.add(textFieldNumbers);

  container.add(labelLetters3);
  container.add(textFieldLetters3);

  container.add(labelLetters35);
  container.add(textFieldLetters35);

     frame.getContentPane().add(container);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(225, 250);
     frame.setVisible(true);
 }

 public static void main(String[] args) {

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }
}

Y eso es todo por hoy si os han quedado cuestiones sin resolver sobre este tema, no dudéis en utilizar la página de Consultas ¿Necesitas ayuda con tu código? Para poneros en contacto conmigo.

viernes, 23 de marzo de 2012

Escalar imagen con Swing, Zoom a una imagen

3 comentarios
Vamos a ver como agrandar o reducir el zoom de una imagen con Swing. Esta entrada es una sugerencia de un compañero, por lo que aprovecho para deciros que si tenéis alguna sugerencia sobre lo que os gustaría que escribiésemos no tenéis más que decirlo.

Antes de explicar como lo hemos hecho, informamos de que desde el JDK1.1 la clase Image tiene un método de conveniencia llamado getScaledInstace() que devolverá una versión a escala de la imagen de acuerdo a las dimensiones proporcionadas.

Esta método además acepta un "hint" de los 5 que exponemos a continuación:

  • SCALE_REPLICATE: Mucho rendimiento, poca calidad, podemos obtener imágenes pixeladas
  • SCALE_FAST: Es sinónimo de SCALE_REPLICATE.
  • SCALE_AREA_AVERAGING: Es más lento que los anteriores pero proporciona más calidad.
  • SCALE_SMOOTH: Es sinónimo de SCALE_AREA_AVERAGING.
  • SCALE_DEFAULT: Tomaremos el valor por defecto, que será SCALE_FAST.


Más adelante con la introducción del JDK1.2, se nos ofrecieron clases como la BufferredImage y Graphics2D y unos “hint” más flexibles gracias a RenderingHints. A diferencia de los antiguos "hints" estos nos proporcionan un mayor control sobre el tratamiento de las imágenes cuando llamamos a Graphics2D.drawImage(). Estas claves ya las explicamos con anterioridad por lo que si queréis saber más sobre esto es conveniente que reviséis la entrada sobre RenderingHints que escribimos con anterioridad.

RenderingHints.KEY_INTERPOLATION:

  • VALUE_INTERPOLATION_NEAREST_NEIGHBOR: Mucho rendimiento, pero baja calidad.
  • VALUE_INTERPOLATION_BILINEAR: Es algo más lento pero proporciona mayor calidad.
  • VALUE_INTERPOLATION_BICUBIC: Es similar a BILINEAR pero utiliza más muestras a la hora de filtrar las imágenes y suele ofrecer más calidad.

RenderingHints.KEY_RENDER_QUALITY: 

  • VALUE_RENDER_SPEED: Significa “Prefiero velocidad antes que calidad, pero lo dejo en tus manos”, es sinónimo de VALUE_INTERPOLATION_NEAREST_NEIGHBOR.
  • VALUE_RENDER_QUALITY: “Prefiero calidad antes que velocidad, lo dejo en tus manos”, sería como usar VALUE_INTERPOLATION_BILINEAR.
  • VALUE_RENDER_DEFAULT: Significa “no me importa, hazlo sin más”, sinónimo de VALUE_RENDER_SPEED.


Usando RenderintHints, BufferedImage y Graphics2D obtendremos mejores resultados, más velocidad y calidad, por lo que será el método que utilizaremos en nuestro ejemplo.

Pasamos a mostrar el código y dado que es algo complejo explicaremos que hace cada método. En principio simplemente crearemos un panel con un par de botones para aumentar y reducir el nivel de zoom aplicado, y un panel donde pintaremos nuestra imagen con el zoom deseado.
Además de esto necesitamos algún método que nos transforme nuestro objeto Image a BufferredImage, para ello nos serviremos de toBufferredImage(Image imagen) y hasAlpha(Image imagen). Este último nos dirá si la imagen contiene píxeles transparentes ya que la manera de crear la imagen en este caso será distinta.

Después de esto solo nos queda sobreescribir el método paintComponent() del panel donde mostraremos la imagen para utilizar los parámetros de nivel de zoom y claves de renderizado que elegimos y ya lo tendremos todo armado.

package imageZoom;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Transparency;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber;
import java.net.URL;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JPanel;

public class ZoomPanel extends JPanel {

 private BufferedImage originalImage;

 public ZoomPanel(String pathToImage) {
  originalImage = toBufferedImage(createImage(pathToImage).getImage());
  initPanel();
 }

 private void initPanel(){
  final Zoom zoom = new Zoom();

  JButton plus = new JButton(new AbstractAction("+") {

   @Override
   public void actionPerformed(ActionEvent e) {
    zoom.increaseZoom();
   }
  });
  JButton minus = new JButton(new AbstractAction("-") {

   @Override
   public void actionPerformed(ActionEvent e) {
    zoom.decreaseZoom();
   }
  });


  this.setLayout(new BorderLayout());
  this.add(minus, BorderLayout.WEST);
  this.add(zoom, BorderLayout.CENTER);
  this.add(plus, BorderLayout.EAST);

 }

 private class Zoom extends JPanel {

  private float xScaleFactor = 1;
  private float yScaleFactor = 1;

  public void increaseZoom() {
   xScaleFactor+= 0.1;
   yScaleFactor+= 0.1;
   repaint();
  }

  public void decreaseZoom() {
   xScaleFactor-= 0.1;
   yScaleFactor-= 0.1;
   repaint();
  }


  @Override
  public void paintComponent(Graphics g) {
   Graphics2D g2 = (Graphics2D) g;
   int newW = (int) (originalImage.getWidth() * xScaleFactor);
   int newH = (int) (originalImage.getHeight() * yScaleFactor);
   g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
   g2.clearRect(0, 0, getWidth(), getHeight());
   g2.drawImage(originalImage, 0, 0, newW, newH, null);
  }

 }

 private ImageIcon createImage(String path) {
  URL imgURL = getClass().getResource(path);
     if (imgURL != null) {
         return new ImageIcon(imgURL);
     } else {
         System.err.println("Couldn't find file: " + path);
         return null;
     }
 }


 /**
  * Obtiene una BufferedImage a partir de una Image.
  * return BufferredImage bi
  */
 public static BufferedImage toBufferedImage(Image image) {
  if (image instanceof BufferedImage) {
   return (BufferedImage) image;
  }

  image = new ImageIcon(image).getImage();

  boolean hasAlpha = hasAlpha(image);

  BufferedImage bimage = null;
  GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  try {

   int transparency = Transparency.OPAQUE;
   if (hasAlpha) {
    transparency = Transparency.BITMASK;
   }

   GraphicsDevice gs = ge.getDefaultScreenDevice();
   GraphicsConfiguration gc = gs.getDefaultConfiguration();
   bimage = gc.createCompatibleImage(image.getWidth(null), image.getHeight(null), transparency);
  } catch (HeadlessException e) {
   // The system does not have a screen
  }

  if (bimage == null) {
   int type = BufferedImage.TYPE_INT_RGB;
   if (hasAlpha) {
    type = BufferedImage.TYPE_INT_ARGB;
   }
   bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
  }

  Graphics g = bimage.createGraphics();

  g.drawImage(image, 0, 0, null);
  g.dispose();

  return bimage;
 }

 /**
  * Devuelve true si una imagen tiene pixeles transparentes.
  *
  * @param image a testear
  * @return true si tiene pixeles transparentes
  */
 public static boolean hasAlpha(Image image) {

  if (image instanceof BufferedImage) {
   BufferedImage bimage = (BufferedImage) image;
   return bimage.getColorModel().hasAlpha();
  }

  PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
  try {
   pg.grabPixels();
  } catch (InterruptedException e) {
  }

  ColorModel cm = pg.getColorModel();
  return cm.hasAlpha();
 }

}

Lo que obtendríamos sería algo similar a esto que os muestro.




Para lanzar la aplicación de ejemplo, lanzar el código siguiente sustituyendo "images/ER.jpg" por una imagen de vuestro equipo y lo podréis ver en acción.

package imageZoom;

import java.awt.Dimension;

import javax.swing.JFrame;

public class Launch {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Zoom a imagen");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  ZoomPanel zoom = new ZoomPanel("images/ER.jpg");

  frame.add(zoom);
  frame.setPreferredSize(new Dimension(485,300));

  frame.pack();
  frame.setVisible(true);
 }

 public static void main(String[] args) {
  // Schedule a job for the event dispatch thread:
  // creating and showing this application's GUI.
  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }

}


Saludos y hasta pronto, ya sabéis que se agradecen los comentarios.

miércoles, 21 de marzo de 2012

GradientPaint demo, ejemplos del uso de gradientes en java

4 comentarios
Hoy hablaremos de los gradientes o degradados de color tan usados como relleno del fondo en multitud de situaciones.
Para ponernos en situación explicaremos primero que es un gradiente.
Básicamente un gradiente nos permite rellenar una forma con una degradación lineal del color. Es decir, si tenemos un punto A con un color X y un punto B con otro color Y, el espacio entre ambos puntos va cambiando progresivamente de color desde el color X al color Y.

Una vez aclarado esto, paso a enseñaros unos ejemplos del uso de gradientes. Para crearlos haremos uso de la clase GradientPaint que nos permite aplicar esta degradación de color entre 2 puntos que explicábamos arriba.
Los constructores de esta clase nos permiten especificar los 2 puntos y los 2 colores que utilizaremos para crear el gradiente.

GradientPaint gp = new GradientPaint(Point2D pt1, Color color1, Point2D pt2, Color color2, boolean cyclic);

Estos puntos podemos especificarlos mediante un objeto Point o bien mediante coordenadas:

GradientPaint gp = new GradientPaint(float x1, float y1, Color color1, float x2, float y2, Color color2, boolean cyclic);

El último parámetro indica si el gradiente debe repetirse en todo el espacio de manera cíclica o si fuera del espacio contenido entre ambos puntos el color debe ser sólido.

Pasamos ahora a ver algunos ejemplos del uso de gradientes.

Gradiente horizontal

package gradientPaintDemo;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

public class HorizontalGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /*
   * Para crear un gradiente horizontal,
   * las coordenadas a usar seran del {0,0} al {anchura del componente, 0}
   */
  GradientPaint horizontalGradient = new GradientPaint(0, 0, Color.RED, getWidth(), 0, Color.BLUE);
  g2d.setPaint(horizontalGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());

 }

}

Gradiente vertical

package gradientPaintDemo;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

public class VerticalGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /*
   * Para crear un gradiente vertical,
   * las coordenadas a usar seran del {0,0} al {0, altura del componente}
   */
  GradientPaint verticalGradient = new GradientPaint(0, 0, Color.GREEN, 0, getHeight(), Color.YELLOW);
  g2d.setPaint(verticalGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());

 }

}

Gradiente diagonal


package gradientPaintDemo;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

public class DiagonalGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /*
   * Para crear un gradiente diagonal,
   * las coordenadas a usar seran del {0,0} al {anchura del componente / ,
   * altura del componente / 2}
   */
  GradientPaint diagonalGradient = new GradientPaint(0, 0, Color.GRAY, getWidth() / 2, getHeight() / 2, Color.ORANGE);
  g2d.setPaint(diagonalGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());

 }

}

Gradiente redondo
Este degradado es un tanto especial, no usaremos la clase GradientPaint para crearlo, en lugar de eso implementaremos nuestro propio Paint. La interfaz Paint define como generar los patrones de color usados para operar con Graphics2D. Este ejemplo no es tan fácil de entender como los demás que exponemos en esta entrada, pero podéis reutilizar el código a placer.
package gradientPaintDemo;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import javax.swing.JPanel;

public class RoundGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /* Para crear un gradiente circular. */
  RoundGradient roundGradient = new RoundGradient(getWidth() / 2, getHeight() / 2, Color.PINK, new Point(0, 100), Color.CYAN);
  g2d.setPaint(roundGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());
 }

 private class RoundGradient implements Paint {

  protected Point2D point;

  protected Point2D mRadius;

  protected Color color1, color2;

  public RoundGradient(double x, double y, Color color1, Point2D radius, Color color2) {
   if (radius.distance(0, 0) <= 0) {
    throw new IllegalArgumentException("Radius must be greater than 0.");
   }
   point = new Point2D.Double(x, y);
   this.color1 = color1;
   mRadius = radius;
   this.color2 = color2;
  }

  @Override
  public int getTransparency() {
   int a1 = color1.getAlpha();
   int a2 = color2.getAlpha();
   return (((a1 & a2) == 0xff) ? OPAQUE
          : TRANSLUCENT);
  }

  @Override
  public PaintContext createContext(ColorModel cm, Rectangle deviceBounds, Rectangle2D userBounds,
    AffineTransform xform, RenderingHints hints) {
   Point2D transformedPoint = xform.transform(point, null);
   Point2D transformedRadius = xform.deltaTransform(mRadius, null);
   return new RoundGradientContext(transformedPoint, color1, transformedRadius, color2);
  }

 }

 private class RoundGradientContext implements PaintContext {
  protected Point2D mPoint;

  protected Point2D mRadius;

  protected Color color1, color2;

  public RoundGradientContext(Point2D p, Color c1, Point2D r, Color c2) {
   mPoint = p;
   color1 = c1;
   mRadius = r;
   color2 = c2;
  }

  public void dispose() {
  }

  public ColorModel getColorModel() {
   return ColorModel.getRGBdefault();
  }

  public Raster getRaster(int x, int y, int w, int h) {
   WritableRaster raster = getColorModel().createCompatibleWritableRaster(w, h);

   int[] data = new int[w * h * 4];
   for (int j = 0; j < h; j++) {
    for (int i = 0; i < w; i++) {
     double distance = mPoint.distance(x + i, y + j);
     double radius = mRadius.distance(0, 0);
     double ratio = distance / radius;
     if (ratio > 1.0)
      ratio = 1.0;

     int base = (j * w + i) * 4;
     data[base + 0] = (int) (color1.getRed() + ratio * (color2.getRed() - color1.getRed()));
     data[base + 1] = (int) (color1.getGreen() + ratio * (color2.getGreen() - color1.getGreen()));
     data[base + 2] = (int) (color1.getBlue() + ratio * (color2.getBlue() - color1.getBlue()));
     data[base + 3] = (int) (color1.getAlpha() + ratio * (color2.getAlpha() - color1.getAlpha()));
    }
   }
   raster.setPixels(0, 0, w, h, data);

   return raster;
  }
 }

}


Gradiente horizontal cíclico

package gradientPaintDemo;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

public class HorizontalCyclicGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /*
   * Para crear un gradiente horizontal cíclico, simplemente especificar
   * la anchura del color
   * y crear el gradiente con la propiedad cíclica a "true"
   */
  GradientPaint diagonalGradient = new GradientPaint(0, 0, Color.RED, 20, 0, Color.BLUE, true);
  g2d.setPaint(diagonalGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());

 }

}

Gradiente vertical cíclico

package gradientPaintDemo;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;

import javax.swing.JPanel;

public class VerticalCyclicGradientPanel extends JPanel {

 @Override
 public void paint(Graphics g) {
  Graphics2D g2d = (Graphics2D) g;

  /*
   * Para crear un gradiente horizontal cíclico, simplemente especificar
   * la altura del color
   * y crear el gradiente con la propiedad cíclica a "true"
   */
  GradientPaint diagonalGradient = new GradientPaint(0, 0, Color.GREEN, 0, 20, Color.YELLOW, true);
  g2d.setPaint(diagonalGradient);

  g2d.fillRect(0, 0, getWidth(), getHeight());

 }

}

Este sería el resultado de juntar todos estos ejemplos en un panel.


Para ver todo esto en funcionamiento, aquí os dejo la clase que carga la aplicación.

package gradientPaintDemo;

import java.awt.GridLayout;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class GradientsDemo extends JPanel {

 private static void createAndShowGUI() {
  // Crea y prepara la ventana.
  JFrame frame = new JFrame("Ejemplos de gradientes");
  frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

  frame.getContentPane().setLayout(new GridLayout(3, 3, 10, 10));

  frame.getContentPane().add(new HorizontalGradientPanel());
  frame.getContentPane().add(new VerticalGradientPanel());
  frame.getContentPane().add(new DiagonalGradientPanel());
  frame.getContentPane().add(new RoundGradientPanel());
  frame.getContentPane().add(new HorizontalCyclicGradientPanel());
  frame.getContentPane().add(new VerticalCyclicGradientPanel());

  frame.setSize(400, 400);
  frame.setVisible(true);
 }

 public static void main(String[] args) {
  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });

 }
}

Eso es todo por hoy, espero que lo aquí expuesto os haya sido útil de alguna manera.

Un saludo

miércoles, 7 de marzo de 2012

Java GUI, Swing o AWT

0 comentarios
Muchas veces ante la necesidad de crear una interfaz gráfica nos vemos en la necesidad de decidir si utilizar Swing o AWT. Bien, veamos similitudes y diferencias entre uno y otro.

Un repaso a AWT

AWT son las siglas de Abstract Window ToolKit, las herramientas originales de Java para crear interfaces gráficas.
Es una librería GUI portable para aplicaciones autónomas y/o applets, proporciona la conexión entre nuestra aplicación y el GUI nativo.

Las prestaciones de AWT incluyen:

  • Un amplio grupo de componentes de usuario
  • Un modelo de manejo de eventos robusto
  • Herramientas gráficas y de imágenes (clases de Formas, colores y fuentes)
  • Manejadores de diseño que no dependen del tamaño de pantalla o resolución
  • Clases de transferencia de datos, para copiar-pegar a través del portapapeles de la plataforma

Los componentes de AWT dependen de componentes de código nativo, por lo que a los componentes se les suele llamar “heavyweight components” (componentes pesados).
AWT está pensado para aplicaciones que corran en navegadores antiguos y definido con los mínimos de cualquier aplicación, es por esto que no incluye componentes complejos como pueden ser tablas, vistas de árbol, barras de progreso y otros.

Un vistazo a Swing


Swing implementa un juego de componentes construidos sobre AWT y además proporciona un “look and feel” conectable/intercambiable. Está escrito 100% en código Java y basado en el framework Lightweight UI de la JDK 1.1.

Sus características incluyen:

  • Todas las prestaciones de AWT
  • Componentes 100% Java de las versiones de los componentes de AWT
  • Un rico conjunto de componentes de alto nivel (listas en árbol, paneles de pestañas, etc...)
  • Un diseño Java puro, no depende de terceros.
  • Look and feel intercambiable

Al no depender de componentes de la plataforma, a los componentes de Swing se les llama “lightweight components”.
Swing es parte de las JFC (Java Foundation Classes), se creó para solucionar muchas de las limitaciones de AWT. Swing se creó como un bien diseñado, flexible y poderoso juego de herramientas GUI.

Si nos paramos a comparar sus componentes, vemos que son bastante similares. Os dejo una tabla con la correspondencia de algunos componentes AWT con su análogo Swing.

AWTSwing
AppletJApplet
FrameJFrame
WindowJWindow
DialogJDialog
ComponentJComponent
PanelJPanel
ButtonJButton
CanvasPanel
CheckboxJCheckBox o JRadioButton
ChoiceJComboBox
LabelJLabel
ListJList
TextAreaJTextArea
TextFieldJTextField
MenuJMenu
MenuItemJMenuItem

Como conclusión, podríamos decir que si queremos que nuestra aplicación corra en cualquier entorno, independientemente de la antigüedad del mismo, deberíamos usar AWT; ahora bien, si lo que queremos es una herramienta potente, flexible, usar tablas y otros componentes complejos; y completamente adaptable a nuestras necesidades, desde luego nuestra decisión está clara, usaremos la tecnología Swing.

Como punto final vamos a ver como crear un programa sencillo, un panel al que añadimos 2 botones, usando ambas tecnologías para poder ver las diferencias.

Ejemplo con AWT
package AWTvsSwing;

import java.applet.Applet;
import java.awt.Button;
import java.awt.FlowLayout;

public class MainAWT extends Applet {

 @Override
 public void init() {

        Button button1 = new Button();

        button1.setLabel("My Button 1");

        Button button2 = new Button("My Button 2");

        setLayout(new FlowLayout());

        add(button1);
        add(button2);
 }

}


Ejemplo con Swing
package AWTvsSwing;

import java.awt.FlowLayout;

import javax.swing.JApplet;
import javax.swing.JButton;

public class MainSwing extends JApplet {

 @Override
 public void init() {
  JButton button1 = new JButton();

        button1.setText("My Button 1");

        JButton button2 = new JButton("My Button 2");

        setLayout(new FlowLayout());

        add(button1);
        add(button2);
 }

}

lunes, 20 de febrero de 2012

Bordes Java Swing, BorderFactory y ejemplos

1 comentarios
A cualquier JComponent se le puede poner uno o mas bordes. Un objeto Border sabe como dibujar los límites de un componente Swing y además de para dibujar lineas o cualquier otro aspecto visual, se pueden utilizar por ejemplo para dar mayor tamaño a los componentes.

Para establecer un borde a un componente debemos usar el método setBorder(Border border).
En otro tutorial ya enseñamos con anterioridad como definir nuestros propios bordes, hoy os enseñaré el uso de la clase BorderFactory que nos permite crear la mayoría de los tipos de bordes que Swing puede crear.

Veamos unos ejemplos de como crear los bordes mas comunes y las opciones que tenemos. En el código podéis leer comentarios sobre que tipos de bordes podemos crear con BorderFactory.

package borderFactory;

import java.awt.Color;
import java.awt.Container;
import java.net.URL;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;

public class BorderSamples extends JPanel {

 /**
  * Constructor.
  */
 public BorderSamples() {
  super();
  addComponents();
 }

 /**
  * Añade los componentes.
  */
 private void addComponents() {

  // Borde vacio con 10px por cada lado
  Border empty = BorderFactory.createEmptyBorder(10, 10, 10, 10);

  /** Bordes simples */
  // Borde de color rojo con grosor de linea de 2px
  Border line = BorderFactory.createLineBorder(Color.RED, 2);
  // Bordes biselado de varios colores
  Border bevelRaised = BorderFactory.createBevelBorder(BevelBorder.RAISED, Color.BLUE, Color.GREEN, Color.BLACK, Color.RED);
  Border bevelLowered = BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.BLUE, Color.GREEN, Color.BLACK, Color.RED);
  // Bordes grabados de varios colores
  Border etchedRaised = BorderFactory.createEtchedBorder(EtchedBorder.RAISED, Color.RED, Color.GRAY);
  Border etchedLowered = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED, Color.RED, Color.GRAY);

  /** Bordes mate */
  /* En este tipo de bordes podemos elegir el grosor de cada uno de los lados y el color con el que pintarlo*/
  Border matte  = BorderFactory.createMatteBorder(3, 3, 5, 5, Color.ORANGE);
  URL imgURL = getClass().getResource("images/star.png");
  ImageIcon ico = new ImageIcon(imgURL);
  /* Ademas de elegir el grosor podemos establecer una icono como imagen de fondo del borde. */
  Border matteIco = BorderFactory.createMatteBorder(10, 10, 0, 20, ico);

  /** Bordes compuestos */
  /* El TitledBorder añade una etiqueta al borde que utilizamos para crearlo,
   * podemos elegir la posición del texto */
  Border titled = BorderFactory.createTitledBorder(line, "titulo", TitledBorder.CENTER, TitledBorder.TOP, null, Color.BLUE);
  /* Un CompoundBorder lo creamos por la combinación de 2 bordes simples. */
  Border compound = BorderFactory.createCompoundBorder(titled, etchedRaised);

  // Paneles
  JPanel simplePanel = new JPanel();
  JPanel mattePanel = new JPanel();
  JPanel compoundPanel = new JPanel();
  simplePanel.setLayout(new BoxLayout(simplePanel, BoxLayout.Y_AXIS));
  mattePanel.setLayout(new BoxLayout(mattePanel, BoxLayout.Y_AXIS));
  compoundPanel.setLayout(new BoxLayout(compoundPanel, BoxLayout.Y_AXIS));

  createComponent("LineBorder", line, simplePanel);
  createComponent("EmptyBorder", empty, simplePanel);
  createComponent("BevelBorder elevado", bevelRaised, simplePanel);
  createComponent("BevelBorder rebajado", bevelLowered, simplePanel);
  createComponent("EtchedBorder elevado", etchedRaised, simplePanel);
  createComponent("EtchedBorder rebajado", etchedLowered, simplePanel);
  createComponent("MatteBorder", matte, mattePanel);
  createComponent("MatteBorder con icono", matteIco, mattePanel);
  createComponent("TitledBorder", titled, compoundPanel);
  createComponent("CompoundBorder", compound, compoundPanel);

  simplePanel.setBorder(BorderFactory.createTitledBorder("Bordes simples"));
  mattePanel.setBorder(BorderFactory.createTitledBorder("Bordes mate"));
  compoundPanel.setBorder(BorderFactory.createTitledBorder("Bordes compuestos"));
  this.add(simplePanel);
  this.add(mattePanel);
  this.add(compoundPanel);
 }

 /**
  * Crea un panel al que le inserta un label con el texto especificado,
  * le establece el borde deseado y lo inserta
  * en el contenedor que le pasamos como parametro
  * @param text Texto de la etiqueta
  * @param border Borde a establecer
  * @param container Contenedor a usar
  */
 private void createComponent(String text, Border border, Container container) {
  JPanel panel = new JPanel();
  JLabel label = new JLabel(text);
  label.setBorder(border);

  panel.add(label);
  container.add(panel);
 }

}

Este sería el resultado de aplicar a las etiquetas creadas cada uno de los distintos bordes.


Y para lanzar la aplicación de ejemplo.

package borderFactory;

import javax.swing.JFrame;


public class Main {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Ejemplos de uso de BorderFactory");

  BorderSamples panel = new BorderSamples();

     frame.getContentPane().add(panel);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(350, 450);
     frame.setVisible(true);
 }

 public static void main(String[] args) {

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }
}


Por supuesto hay muchas más opciones que explotar, pero esa tarea de investigación la dejo para vosotros :)
Esto es todo por hoy, gracias por leerme y espero haberos sido de ayuda.

jueves, 9 de febrero de 2012

BorderLayout y GridLayout, ejemplos de layouts en java

4 comentarios
Hola a todos, hoy vamos a hablar sobre la manera en la que disponemos los componentes Swing en los contenedores de nuestras aplicaciones. Es aquí donde los LayoutManager entran en juego. Por ejemplo cuando hacemos:

JPanel panel = new JPanel();
for(int i = 0; i < 10; i++) {
 panel.add(new JLabel("Etiqueta " + i));
}
Las etiquetas que estamos añadiendo se disponen en fila de izquierda a derecha, esto es porque al crear un panel, por defecto tiene establecido un FlowLayout. Este dispondrá de esta manera los componentes, con una separación de 10 pixeles entre componentes hasta que no quepan mas componentes en la misma línea, momento en el cual saltará a la siguiente. Pero no debemos limitarnos a la manera de disponer los componentes por defecto, vamos a pasar a explicar dos de los LayoutManager que mas se usan. Estos son: BorderLayout y GridLayout.

BorderLayout 

Una disposición Border, dispone un contenedor ordenando y redimensionando sus componentes para adaptarse a cinco regiones: NORTE, SUR, ESTE, OESTE y CENTRO.
Cada una de estas regiones está definida por constantes propias de la clase BordeLayout:

  • BorderLayout.NORTH 
  • BorderLayout.SOUTH 
  • BorderLayout.EAST 
  • BorderLayout.WEST 
  • BorderLayout.CENTER 
Para utilizarlo, bastará con especificar una de estas constantes como limitación al añadir el componente.

JPanel panel = new JPanel();
//Establecemos el BorderLayout al panel
panel.setLayout(new BorderLayout(10, 10));
panel.add(new JButton(“boton”), BorderLayout.CENTER);
Por conveniencia si no especificamos ninguna de estas constantes el componente se insertará en el centro del contenedor. El constructor BorderLayout(int hgap, int vgap) nos permite especificar el espacio en pixeles que existirá entre los componentes en el contenedor, así por ejemplo para hacer que entre los componentes exista un espacio horizontal de 10 pixeles y otro vertical de 20 pixeles bastaría con:
BorderLayout layout = new BorderLayout(10, 20);
Si no especificamos ningún espacio, por defecto no habrá separación entre los componentes. Veamos un ejemplo real de como usarlo:
package layout.borderLayout;

import java.awt.BorderLayout;

import javax.swing.JButton;
import javax.swing.JPanel;

public class BorderLayoutPanel extends JPanel {

 public BorderLayoutPanel() {
  // establecemos el layout
  this.setLayout(new BorderLayout(5, 10));
  init();
 }

 /**
  * Añadimos componentes
  */
 private void init() {
  JButton north = new JButton("NORTH");
  JButton south = new JButton("SOUTH");
  JButton east = new JButton("EAST");
  JButton west = new JButton("WEST");
  JButton center = new JButton("CENTER");

  this.add(north, BorderLayout.NORTH);
  this.add(south, BorderLayout.SOUTH);
  this.add(east, BorderLayout.EAST);
  this.add(west, BorderLayout.WEST);
  this.add(center, BorderLayout.CENTER);


 }

}
Este código nos creará un panel al que se le establece un BorderLayout con 5 pixeles de separación horizontal entre los componentes y 10 pixeles de separación vertical.
Para verlo en funcionamiento:
package layout.borderLayout;

import javax.swing.JFrame;


public class Main {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Ejemplos de uso de layouts");

  BorderLayoutPanel panel = new BorderLayoutPanel();

     frame.getContentPane().add(panel);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(400, 400);
     frame.setVisible(true);
 }

 public static void main(String[] args) {

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }
}

Y este sería el resultado.



GridLayout

El GridLayout dispone los componentes de un contenedor en una rejilla rectangular. El contenedor se dividirá en rectangulos iguales y en cada uno se colocará uno de los componentes. A la hora de construir este layout debemos especificar el número de filas y columnas que necesitamos para nuestros componentes, para ello usaremos el siguiente constructor GridLayout(int rows, int cols) , además si queremos controlar el espacio que existirá entre los componentes en el contenedor de manera similar a como hicimos con BorderLayout podemos usar este otro: GridLayout(int rows, int cols, int hgap, int vgap). Pasemos a un ejemplo que nos aclaré más las cosas, por ejemplo digamos que queremos disponer 10 JButton en un panel de manera que se distribuyan en 2 filas y 5 columnas, con una separación horizontal de 15 pixeles y una separación vertical de 5 pixeles. Aquí os dejo el código que configuraría el panel:
package layout.gridLayout;

import java.awt.GridLayout;

import javax.swing.JButton;
import javax.swing.JPanel;

public class GridLayoutPanel extends JPanel {

 public GridLayoutPanel() {
  // establecemos el layout
  this.setLayout(new GridLayout(2, 5, 15, 5));
  init();
 }

 /**
  * Añadimos componentes
  */
 private void init() {

  for(int i = 0; i < 10; i++) {
   this.add(new JButton("Boton " +  i));
  }

 }

}
Para verlo en funcionamiento:
package layout.gridLayout;

import javax.swing.JFrame;


public class Main {

 private static void createAndShowGUI() {
  JFrame frame = new JFrame("Ejemplos de uso de layouts");

  GridLayoutPanel panel = new GridLayoutPanel();

     frame.getContentPane().add(panel);
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
     frame.setSize(500, 400);
     frame.setVisible(true);
 }

 public static void main(String[] args) {

  javax.swing.SwingUtilities.invokeLater(new Runnable() {
   public void run() {
    createAndShowGUI();
   }
  });
 }
}
Y este sería el aspecto que tendría este contenedor.




Podríamos hablar largo y tendido sobre LayoutManager en Java, pero esto será tema de siguiente artículos.

Un saludo y gracias por leerme.

miércoles, 18 de enero de 2012

JButton, JCheckBox, JComboBox en JTable

11 comentarios

El componente JTable es uno de los mas complejos que nos ofrece el paquete Swing en cuanto a diseño de interfaces se refiere, precisamente por su complejidad también permite una personalización casi absoluta del componente.

La clase JTable sirve para mostrar tablas de datos, permitiendo opcionalmente al usuario editar los datos. Los JTable no contienen ni almacenan datos, simplemente es una manera de mostrarlos.
Los JScrollPane son los componentes que habitualmente sirven como contenedor para los JTable, este automáticamente coloca la cabecera de la tabla en la parte superior de la vista de manera que los nombres de las columnas permanecen visibles cuando hacemos “scroll”.

El encargado de decirnos como se muestra un valor en cada celda es el TableCellRenderer, esta interfaz define el método requerido por un objeto que querría ser dibujado en las celdas de un JTable.
Así mismo, el encargado de decirnos como se comporta cada celda es el TableCellEditor, una interfaz que define el método que cualquier objeto que querría ser editor de valores de un JTable necesita implementar.

Otro de los componentes mas importantes es el TableModel, esta interfaz especifica los métodos que el JTable usará para interrogar a un modelo de datos tabulado. Un JTable puede configurarse para mostrar cuanlquier modelo de datos que implemente esta interfaz con solo un par de líneas de código:

MyTableModel model = new MyTableModel(columnNames, data);
// Establecemos el modelo
JTable table = new JTable(model);

En nuestro ejemplo de hoy, explicamos como mostrar componentes complejos como JCheckBox, JComboBox y JButton en el interior de nuestras tablas.

En primer lugar definiremos nuestro TableModel, en este estableceremos como queremos que nuestra tabla se comporte.

package tablaCompleja.componentes;

import javax.swing.table.AbstractTableModel;

public class MyTableModel extends AbstractTableModel {

 /** Nombre de las columnas. */
 private String[] columnNames;
 /** Datos. */
 private Object[][] data;

 /**
  * Constructor.
  * @param columnNames Nombres de las columnas
  * @param data Datos de la tabla
  */
 public MyTableModel(String[] columnNames, Object[][] data) {
  this.columnNames = columnNames;
  this.data = data;
 }

 @Override
 public String getColumnName(int column) {
  // Nombre de las columnas para la cabecera
  return columnNames[column];
 }

 @Override
 public int getRowCount() {
  // Devuelve el número de filas
  return data != null ? data.length : 0;
 }

 @Override
 public int getColumnCount() {
  // Devuelve el número de columnas
  return columnNames.length;
 }

 /**
  * Nos devolverá la clase que contiene cada columna,
  * es necesario para trabajar correctamente con los componentes
  * que mostraremos en la tabla.
  */
 @Override
 public Class getColumnClass(int columnIndex) {
  Class clazz = Object.class;

  Object aux = getValueAt(0, columnIndex);
  if (aux != null) {
   clazz = aux.getClass();
  }

  return clazz;
 }

 @Override
 public Object getValueAt(int rowIndex, int columnIndex) {
  // Devuelve el valor que se debe mostrar en la tabla en cada celda
  return data[rowIndex][columnIndex];
 }

    @Override
 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
     // Si queremos que la tabla sea editable deberemos establecer estos valores
     data[rowIndex][columnIndex] = aValue;
     fireTableCellUpdated(rowIndex, columnIndex);
    }

 @Override
 public boolean isCellEditable(int rowIndex, int columnIndex) {
  // Permitimos editar todas las celdas de la tabla
  return true;
 }

 /**
  * Nos servira para limpiar la información de una fila
  * @param row
  */
 public void reset(int row) {

  for (int i = 0; i < data[row].length - 1; i++) {
   // Para las columnas con String
   if (getColumnClass(i) == String.class) {
    setValueAt("", row, i);
   } else if(getColumnClass(i) == Boolean.class) {
    setValueAt(false, row, i);
   }
  }

 }

}

Vamos a crear un JTable personalizado que heredará del JTable original, en esta clase vamos a ver como especificar que renderers y editors usaremos para cada columna dependiendo del tipo de objeto que contenga.
package tablaCompleja;

import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JTable;

import tablaCompleja.componentes.ButtonCellEditor;
import tablaCompleja.componentes.ButtonCellRenderer;
import tablaCompleja.componentes.MyTableModel;

public class MyTable extends JTable {

 /**
  * Constructor.
  */
 public MyTable() {

  this.setRowHeight(30);

  JComboBox states = new JComboBox(new String[]
                                     {"España",
                                     "Argentina",
                                     "EEUU"}
                                     );

  String [] columnNames = new String[]{ "Nombre", "email", "Fumador", "Nacionalidad", "" };
        Object [][] data      = new Object[][]{
          {"Angel", "angelarcosheredia@gmail.com", false, "Click para elegir", new JButton("Reset")},
          {"Juan", "juan@gmail.com", false, "Click para elegir", new JButton("Reset")},
          {"Ana", "ana@hotmail.com", false, "Click para elegir", new JButton("Reset")}
          };

        MyTableModel model = new MyTableModel(columnNames, data);

        // Establecemos el modelo
        this.setModel(model);

        // Establecemos el renderer y editor que usaremos para el boton
        this.setDefaultRenderer(JButton.class, new ButtonCellRenderer());
        this.setDefaultEditor(JButton.class, new ButtonCellEditor());

        // Editores para cada tipo de objeto, estos nos permitirán darles el comportamiento adecuado
        this.getColumn("Nacionalidad").setCellEditor(new DefaultCellEditor(states));
        this.setDefaultEditor(JCheckBox.class, new DefaultCellEditor(new JCheckBox()));

 }

}
Como vemos en el código anterior los valores boolean se muestran automáticamente como un JCheckBox, y el DefaultCellEditor que es el editor por defecto que usa un JTable también se encargará de utilizar un JComboBox apropiadamente si se lo especificamos. Para el renderizar y darle comportamiento a un JButton si tendremos que implementar nuestro propio renderer y editor. Aquí os dejo el código que, como podéis ver, no es nada complicado.
package tablaCompleja.componentes;

import java.awt.Component;

import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;

public class ButtonCellRenderer implements TableCellRenderer {

 @Override
 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
   int row, int column) {

  // Devolvemos el botón tal cual
  if (value instanceof JButton) {
   return (JButton) value;
  }

  return null;

 }

}
package tablaCompleja.componentes;

import java.awt.Component;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;

public class ButtonCellEditor extends AbstractCellEditor implements TableCellEditor {

 /** Componente que estamos editando. */
 private Component currentValue;

 @Override
 public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) {

  JButton button = null;

  if (value instanceof JButton) {
   button = (JButton) value;
   // Action que permite "limpiar" los valores de una fila
   button.setAction(new AbstractAction("Reset") {

    @Override
    public void actionPerformed(ActionEvent e) {
     ((MyTableModel) table.getModel()).reset(row);

    }
   });
  }

  currentValue = button;

  return button;
 }

 @Override
 public Object getCellEditorValue() {
  return currentValue;
 }

}
Y ahora ya solo nos queda el fragmento de código para echar a andar nuestro ejemplo:
package tablaCompleja.componentes;

import java.awt.Component;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.table.TableCellEditor;

public class ButtonCellEditor extends AbstractCellEditor implements TableCellEditor {

 /** Componente que estamos editando. */
 private Component currentValue;

 @Override
 public Component getTableCellEditorComponent(final JTable table, Object value, boolean isSelected, final int row, int column) {

  JButton button = null;

  if (value instanceof JButton) {
   button = (JButton) value;
   // Action que permite "limpiar" los valores de una fila
   button.setAction(new AbstractAction("Reset") {

    @Override
    public void actionPerformed(ActionEvent e) {
     ((MyTableModel) table.getModel()).reset(row);

    }
   });
  }

  currentValue = button;

  return button;
 }

 @Override
 public Object getCellEditorValue() {
  return currentValue;
 }

}

Espero haberos aclarado un poco más como funciona un JTable y que todo esto os haya sido útil de alguna manera.

Un saludo y hasta pronto.