junio 2010

Swing JTables de Java no está preparado para manejar actualizaciones frecuentes con un gran número de columnas y filas. He creado una versión más ligera de JTable llamada FastJTable basada en el código de la Java Christmas Tree.

This implementation is pretty faster when handling huge amounts of data. Consider taking a look at how the memory works if this is your use case.

/**
* Based on Sun's CTTable (Christmas Tree):
* https://java.sun.com/products/jfc/tsc/articles/ChristmasTree/
*
* @author marias
*/

import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;

import javax.swing.CellRendererPane;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JViewport;
import javax.swing.plaf.basic.BasicTableUI;
import javax.swing.table.TableModel;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* Based on CTTable from Christmas Tree:
* https://java.sun.com/products/jfc/tsc/articles/ChristmasTree/
*
* @author marias marias@emergya.es
*/
public class FastJTable extends JTable {
private static final Log log = LogFactory.getLog(FastJTable.class);
private static final long serialVersionUID = -3218140266706898440L;

private JScrollPane scrollPane;

public FastJTable(TableModel model) {
super(model);
}

public void updateUI() {
super.updateUI();
setUI(new FastTableUI(this));
}

private static class FastTableUI extends BasicTableUI {

public FastTableUI(FastJTable table) {
super();
installUI(table);
}

@Override
public void installUI(JComponent c) {
// Overriden to install our own CellRendererPane
super.installUI(c);
c.remove(rendererPane);
rendererPane = new FastCellRendererPane();
c.add(rendererPane);
}
}

/**
* FastCellRendererPane overrides paintComponent to NOT clone the Graphics
* passed in and NOT validate the Component passed in. This will NOT work if
* the painting code of the Component clobbers the graphics (scales it,
* installs a Paint on it...) and will NOT work if the Component needs to be
* validated before being painted.
*/
private static class FastCellRendererPane extends CellRendererPane {
private static final long serialVersionUID = 4811773663334451913L;
private JViewport viewport;

public FastCellRendererPane() {
super();
}

// Can be ignored, we don't exist in the containment hierarchy.
public void repaint() {
}

@Override
public void paintComponent(Graphics g, Component c, Container p, int x,
int y, int w, int h, boolean shouldValidate) {
try {
if (c == null || !isVisible(new Rectangle(x, y, w, h))) {
log.trace("No lo pintamos (" + c + ")");
return;
}

// if (p != null) {
// Color oldColor = g.getColor();
// g.setColor(p.getBackground());
// g.fillRect(x, y, w, h);
// g.setColor(oldColor);
// }

if (c.getParent() != this) {
this.add(c);
}

c.setBounds(x, y, w, h);

// As we are only interested in using a JLabel as the renderer,
// which does nothing in validate we can override this to do
// nothing, if you need to support components that can do
// layout,
// this will need to be commented out, or conditionally
// validate.
if (!(c instanceof JLabel))
c.validate();

// JComponent jc = (c instanceof JComponent) ? (JComponent) c
// : null;
// jc.setDoubleBuffered(true);

// Don't create a new Graphics, reset the clip and translate
// the origin.
Rectangle clip = g.getClipBounds(c.getBounds());
g.clipRect(x, y, w, h);
g.translate(x, y);
c.paint(g);
g.translate(-x, -y);
g.setClip(clip.x, clip.y, clip.width, clip.height);
c.setBounds(-w, -h, 0, 0);
} catch (Throwable t) {
log.error("Error al pintar el componente de la tabla ", t);
}
}

/**
* We only paint the visible parts of the JTable.
* @param rectangle visible
* @return if it has to be painted on screen
*/
public boolean isVisible(Rectangle rectangle) {
if (viewport == null)
return true;

			Rectangle visRect = viewport.getViewRect();
			int xmin = ((Double) rectangle.getMinX()).intValue();
			int ymin = ((Double) rectangle.getMinY()).intValue();
			int xmax = ((Double) rectangle.getMaxX()).intValue();
			int ymax = ((Double) rectangle.getMaxY()).intValue();
			return (visRect.contains(new Point(xmin, ymin))
					|| visRect.contains(new Point(xmax, ymin))
					|| visRect.contains(new Point(xmin, ymax)) || visRect
					.contains(new Point(xmax, ymax)));
		}
	}
}