FastJTable

Las JTables Swing de Java por defecto suelen estar poco preparadas para manejar actualizaciones frecuentes y un gran número de columnas. Basándome en el código de Java de Christmas Tree he creado una extensión ligera de JTable que va bastante más rápida a la hora de manejar gran cantidad de filas.
Swing JTables of Java by default usually are not prepared to handle frequent updates and a huge number of columns and rows. Basing on the code of the Java Christmas Tree , I created a lighted version of JTable which is pretty faster when handling huge amounts of data.


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

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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.