Swing JTable langsam

fittich

Mitglied
Hallo zusammen,

meine JTable ist leider sehr langsam. Der Scrollbalken und auch der RowHeader hängen immer hinterher, wenn man scrollt. Dieser Effekt tritt nur auf, wenn ich einen Renderer benutze, der so aufgebaut ist:

Java:
public class V8CellRenderer extends JLabel implements TableCellRenderer {
        private static final long serialVersionUID = 1L;
        private static final Color defaultColor = Color.WHITE;
        private static final Color selectedColor = new Color(200, 200, 255);
        private static final Border selectedBorder = new LineBorder(Color.BLACK, 1);
        private static final Border defaultBorder = new EmptyBorder(1, 1, 1, 1);

        public Component getTableCellRendererComponent(JTable table, Object val, boolean isSelected, boolean hasFocus, int row, int column) {
                Color color = defaultColor;
                Border border = defaultBorder;
                
                if(hasFocus || isSelected) {
                        color = selectedColor;
                        border = selectedBorder;
                }
                
                if(val != null){
                        JLabel label = (JLabel)val;
                        setForeground(label.getForeground());
                        setText(label.getText());
                        setToolTipText(label.getToolTipText());
                } else {
                        setText("");
                }
                
                setOpaque(true);
                setBackground(color);
                setBorder(border);
                setHorizontalAlignment(SwingConstants.CENTER);
                setVerticalAlignment(SwingConstants.CENTER);
                
                return this;
        }
}

Kann mir jemand sagen, was so unperformant an dem Renderer ist?
 
der DefaultTableCellRenderer erbt auch von JLabel und überschreibt aus Performance-Gründen einige Methoden,
vielleicht solltest du das auch machen bzw. einfach von diesem erben,

im Quelltext vielleicht noch Infos dazu, falls du ihn nicht selber hast:
[..] This would greatly decrease performance because [..]
So this class overrides [..]
If you write your own renderer, please keep this performance consideration in mind. [..]


Java:
/**
 * The standard class for rendering (displaying) individual cells
 * in a <code>JTable</code>.
 * <p>
 *
 * <strong><a name="override">Implementation Note:</a></strong>
 * This class inherits from <code>JLabel</code>, a standard component class. 
 * However <code>JTable</code> employs a unique mechanism for rendering
 * its cells and therefore requires some slightly modified behavior
 * from its cell renderer.  
 * The table class defines a single cell renderer and uses it as a 
 * as a rubber-stamp for rendering all cells in the table; 
 * it renders the first cell,
 * changes the contents of that cell renderer, 
 * shifts the origin to the new location, re-draws it, and so on.
 * The standard <code>JLabel</code> component was not
 * designed to be used this way and we want to avoid 
 * triggering a <code>revalidate</code> each time the
 * cell is drawn. This would greatly decrease performance because the
 * <code>revalidate</code> message would be
 * passed up the hierarchy of the container to determine whether any other
 * components would be affected.  
 * As the renderer is only parented for the lifetime of a painting operation
 * we similarly want to avoid the overhead associated with walking the
 * hierarchy for painting operations.
 * So this class
 * overrides the <code>validate</code>, <code>invalidate</code>,
 * <code>revalidate</code>, <code>repaint</code>, and
 * <code>firePropertyChange</code> methods to be 
 * no-ops and override the <code>isOpaque</code> method solely to improve
 * performance.  If you write your own renderer,
 * please keep this performance consideration in mind.
 * <p>
 *
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases. The current serialization support is
 * appropriate for short term storage or RMI between applications running
 * the same version of Swing.  As of 1.4, support for long term storage
 * of all JavaBeans<sup><font size="-2">TM</font></sup>
 * has been added to the <code>java.beans</code> package.
 * Please see {@link java.beans.XMLEncoder}.
 *
 * @version 1.42 10/31/05
 * @author Philip Milne 
 * @see JTable
 */
public class DefaultTableCellRenderer extends JLabel
    implements TableCellRenderer, Serializable
{

    protected static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); 
    private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);

    // We need a place to store the color the JLabel should be returned 
    // to after its foreground and background colors have been set 
    // to the selection background color. 
    // These ivars will be made protected when their names are finalized. 
    private Color unselectedForeground; 
    private Color unselectedBackground; 

    /**
     * Creates a default table cell renderer.
     */
    public DefaultTableCellRenderer() {
	super();
	setOpaque(true);
        setBorder(getNoFocusBorder());
    }

    private static Border getNoFocusBorder() {
        if (System.getSecurityManager() != null) {
            return SAFE_NO_FOCUS_BORDER;
        } else {
            return noFocusBorder;
        }
    }

    /**
     * Overrides <code>JComponent.setForeground</code> to assign
     * the unselected-foreground color to the specified color.
     * 
     * @param c set the foreground color to this value
     */
    public void setForeground(Color c) {
        super.setForeground(c); 
        unselectedForeground = c; 
    }
    
    /**
     * Overrides <code>JComponent.setBackground</code> to assign
     * the unselected-background color to the specified color.
     *
     * @param c set the background color to this value
     */
    public void setBackground(Color c) {
        super.setBackground(c); 
        unselectedBackground = c; 
    }

    /**
     * Notification from the <code>UIManager</code> that the look and feel
     * [L&F] has changed.
     * Replaces the current UI object with the latest version from the 
     * <code>UIManager</code>.
     *
     * @see JComponent#updateUI
     */
    public void updateUI() {
        super.updateUI(); 
	setForeground(null);
	setBackground(null);
    }
    
    // implements javax.swing.table.TableCellRenderer
    /**
     *
     * Returns the default table cell renderer.
     *
     * @param table  the <code>JTable</code>
     * @param value  the value to assign to the cell at
     *			<code>[row, column]</code>
     * @param isSelected true if cell is selected
     * @param hasFocus true if cell has focus
     * @param row  the row of the cell to render
     * @param column the column of the cell to render
     * @return the default table cell renderer
     */
    public Component getTableCellRendererComponent(JTable table, Object value,
                          boolean isSelected, boolean hasFocus, int row, int column) {

	if (isSelected) {
	   super.setForeground(table.getSelectionForeground());
	   super.setBackground(table.getSelectionBackground());
	}
	else {
	    super.setForeground((unselectedForeground != null) ? unselectedForeground 
	                                                       : table.getForeground());
	    super.setBackground((unselectedBackground != null) ? unselectedBackground 
	                                                       : table.getBackground());
	}
	
	setFont(table.getFont());

	if (hasFocus) {
            Border border = null;
            if (isSelected) {
                border = UIManager.getBorder("Table.focusSelectedCellHighlightBorder");
            }
            if (border == null) {
                border = UIManager.getBorder("Table.focusCellHighlightBorder");
            }
            setBorder(border);

	    if (!isSelected && table.isCellEditable(row, column)) {
                Color col;
                col = UIManager.getColor("Table.focusCellForeground");
                if (col != null) {
                    super.setForeground(col);
                }
                col = UIManager.getColor("Table.focusCellBackground");
                if (col != null) {
                    super.setBackground(col);
                }
	    }
	} else {
            setBorder(getNoFocusBorder());
	}

        setValue(value); 

	return this;
    }
    
    /*
     * The following methods are overridden as a performance measure to 
     * to prune code-paths are often called in the case of renders
     * but which we know are unnecessary.  Great care should be taken
     * when writing your own renderer to weigh the benefits and 
     * drawbacks of overriding methods like these.
     */

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public boolean isOpaque() { 
	Color back = getBackground();
	Component p = getParent(); 
	if (p != null) { 
	    p = p.getParent(); 
	}
	// p should now be the JTable. 
	boolean colorMatch = (back != null) && (p != null) && 
	    back.equals(p.getBackground()) && 
			p.isOpaque();
	return !colorMatch && super.isOpaque(); 
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void invalidate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void validate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void revalidate() {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(long tm, int x, int y, int width, int height) {}

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void repaint(Rectangle r) { }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     *
     * @since 1.5
     */
    public void repaint() {
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {	
	// Strings get interned...
	if (propertyName=="text") {
	    super.firePropertyChange(propertyName, oldValue, newValue);
	}
    }

    /**
     * Overridden for performance reasons.
     * See the <a href="#override">Implementation Note</a> 
     * for more information.
     */
    public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }


    /**
     * Sets the <code>String</code> object for the cell being rendered to
     * <code>value</code>.
     * 
     * @param value  the string value for this cell; if value is
     *		<code>null</code> it sets the text value to an empty string
     * @see JLabel#setText
     * 
     */
    protected void setValue(Object value) {
	setText((value == null) ? "" : value.toString());
    }
	

    /**
     * A subclass of <code>DefaultTableCellRenderer</code> that
     * implements <code>UIResource</code>.
     * <code>DefaultTableCellRenderer</code> doesn't implement
     * <code>UIResource</code>
     * directly so that applications can safely override the
     * <code>cellRenderer</code> property with
     * <code>DefaultTableCellRenderer</code> subclasses.
     * <p>
     * <strong>Warning:</strong>
     * Serialized objects of this class will not be compatible with
     * future Swing releases. The current serialization support is
     * appropriate for short term storage or RMI between applications running
     * the same version of Swing.  As of 1.4, support for long term storage
     * of all JavaBeans<sup><font size="-2">TM</font></sup>
     * has been added to the <code>java.beans</code> package.
     * Please see {@link java.beans.XMLEncoder}.
     */
    public static class UIResource extends DefaultTableCellRenderer 
        implements javax.swing.plaf.UIResource
    {
    }

}
 
Zuletzt bearbeitet von einem Moderator:
mein unscheinbares Posting komplett überlesen? 😉

> Kann man das eleganter lösen, als das mit JLabels darzustellen?

alternativ ein schlankes Mini-Objekt eigener Klasse mit einer Color, einem String und was immer benötigt wird
 
Es muss nichtmal ein Mini-Objekt sein, es tut ja schon das, was ohnehin im Datenmodell steht - und genau darum geht es: Das werden ja keine JLabels sein?! Der Rest wird ja vom Renderer geregelt. Z.B. kann es ja auch sein, dass die Farbe etwas ist, was gar nicht explizit im Datenmodell auftauchen muss. Als plakativbeispiel: Im Datenmodell steht nur ein float, und im Renderer dann sowas wie
Code:
if (floatValue > 0.75) setBackground(Color.RED);
else setBackground(Color.GREEN);
oder so...
 
mein unscheinbares Posting komplett überlesen? 😉

> Kann man das eleganter lösen, als das mit JLabels darzustellen?

alternativ ein schlankes Mini-Objekt eigener Klasse mit einer Color, einem String und was immer benötigt wird

Nee, hab ich nicht überlesen 🙂 Den Quelltext hab ich zwar selber, aber trotzdem danke! Ich wollte das nur erstmal testen und mich dann noch mal melden 🙂 Sorry!

Es muss nichtmal ein Mini-Objekt sein, es tut ja schon das, was ohnehin im Datenmodell steht - und genau darum geht es: Das werden ja keine JLabels sein?! Der Rest wird ja vom Renderer geregelt. Z.B. kann es ja auch sein, dass die Farbe etwas ist, was gar nicht explizit im Datenmodell auftauchen muss. Als plakativbeispiel: Im Datenmodell steht nur ein float, und im Renderer dann sowas wie
Code:
if (floatValue > 0.75) setBackground(Color.RED);
else setBackground(Color.GREEN);
oder so...

Ich verstehe, was du meinst. Aber aus einem einfachen float oder String kann ich die Beziehung nicht ermitteln. Vielleicht ist die Idee vom Mini-Objekt ja gar nicht schlecht. Auf jeden Fall muss ich irgendwie zu einer Koordinate einen Namen der Beziehung und die entsprechende Farbe speichern, und zwar an der Stelle, an der ich die Matrix aufbaue.
 
da muss ich aber wirklich nochmal protestierend auf mein Posting hinweisen,

das Model hat selbstverständlich hier quasi keine Auswirkungen auf die Geschwindigkeit,
dort JLabel ist nur allgemein unnötig und unschön, aber um ein Feld abzufragen ist es quasi egal, wieviel MB im Speicher hinten dranhängen

über die Geschwindigkeit entscheidet der Renderer, dessen Zeichen-Methoden + validate() & Co. als Anhängsel,
ist auch ein JLabel-Problem, aber nur der Renderer, vorerst ganz egal was das Model macht
 
da muss ich aber wirklich nochmal protestierend auf mein Posting hinweisen,

Du hast ja vollkommen recht 🙂 Ich hatte nur erstmal kurzfristig versucht, mich darum zu drücken, weil mir das kompliziert erschien, die Methoden zu überschreiben ;-)
Aber ich hab die jetzt einfach mal so überschrieben:

Java:
public void validate() {} 
public void revalidate() {} 
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {} 
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {}

Und was soll ich sagen: Jetzt ist alles ganz schnell :-D
Vielen, lieben Dank!
 
bedenke möglichst noch einmal, lediglich von DefaultTableCellRenderer statt von JLabel zu erben
 
man erbt dann wenn man erben muss, richtig,
der DefaultTableCellRenderer erbt genau deswegen von JLabel, weil aus Performance-Gründen Methoden überschrieben werden

ansonsten wäre das nicht nötig, ein Renderer muss nicht selber eine Swing-Komponente wie JLabel sein,
wichtig ist nur was getTableCellRendererComponent() zurückgibt, statt
> return this;
kann das auch
> return xyLabel;
sein oder je nach if/else von ein und denselben Renderer mal JLabel, mal JComboBox zurückgegeben werden

für deinen V8CellRenderer im ersten Posting gilt also quasi, dass die Vererbung von JLabel unnötig ist,
wenn man aber die Methoden zur Performance überschreibt braucht man irgendwo eine SubKlasse von JLabel
 
Zuletzt bearbeitet von einem Moderator:
Eine der (wenigen 😎 ) Schwächen von Java: Es gibt keine private Vererbung. Dass der DefaultTableCellRenderer von JLabel erbt, sollte vermutlich eigentlich nicht "nach außen" sichtbar sein. Er soll ja nur "irgendeine implementierung des Interfaces" sein....
 

Neue Themen


Zurück
Oben