JTable Cell Refresh

tuedel

Aktives Mitglied
Hallo =). Ich melde mich schonwieder ..

So richtig zufrieden bin ich zwar mit dem JWindow aus meinem vorherigen Thread noch nicht, aber mal zu was anderem = ).

Ich habe eine JTable, die ich mit comboboxen und Strings versehen habe und die auf MouseReleased Events Änderungen am TableModel vornimmt. Wenn ich dann aber in der überschriebenen setValueAt(..) methode ein fireTableCellUpdated(row, column) ausführe, wird nicht die entsprechende Zelle neu gezeichnet, sondern die gesamte Tabelle.

Wenn ich also nur vereinzelte Updates habe, oder Tabellenspalten ohnehin gleich bleiben, möchte ich ja nicht die gesamte Tabelle neu zeichnen. Vielleicht habe ich einen Fehler gemacht - daher hier der Quellcode für den Renderer, das Model und die JTable.

Java:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package UI.Tables;

import AppletMain.DefaultResources;
import AppletMain.DefaultResources.DEFAULT_COLORSET;
import AppletMain.Enumarations.OPTIONS;
import MapDrawing.SailingPainter;
import UI.Utility.CustomCheckBox;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JLabel;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.border.EmptyBorder;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;


class JTableOptionsRenderer implements TableCellRenderer {

    private JLabel textLabel = new JLabel();
    private CustomCheckBox cBox = CustomCheckBox.createCustomCheckBox();
    private JSeparator sep = new JSeparator(JSeparator.HORIZONTAL);

    public JTableOptionsRenderer() {
        textLabel.setFont(DefaultResources.getFont(DefaultResources.FONT.META_DATA));
        textLabel.setForeground(DefaultResources.getDefaultColor(DefaultResources.DEFAULT_COLORSET.DATA_COLOR));
        textLabel.setBackground(DefaultResources.getDefaultColor(DefaultResources.DEFAULT_COLORSET.TOGGLE_COLOR_FIRST));
        textLabel.setOpaque(true);
        textLabel.setBorder(new EmptyBorder(3, 0, 0, 0));

        cBox.setBackground(DefaultResources.getDefaultColor(DefaultResources.DEFAULT_COLORSET.TOGGLE_COLOR_FIRST));
    }

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
       
        if (value instanceof Boolean) {
            cBox.setSelected((Boolean) value);
            return cBox;
        } else if (value instanceof OPTIONS){
            textLabel.setText(((OPTIONS) value).toString());
            return textLabel;
        }
        else{
            return sep;
        }
    }
}


/**
 *
 * @author lueder.j
 */
class OptionsTableModel extends AbstractTableModel {

    private Object[][] rows = new Object[OPTIONS.values().length][2];
    private String[] columns = {"checkbox", "Definition"};

    public OptionsTableModel() {
        initRows();
    }

    private void initRows() {
        for (int i = 0; i < OPTIONS.values().length; i++) {

            if (OPTIONS.values()[i] == OPTIONS.NONE) {
                rows[i][0] = "";
                rows[i][1] = "";
            } else {
                if (i == 0) {
                    rows[i][0] = true;
                } else {
                    rows[i][0] = false;
                }
                rows[i][1] = OPTIONS.values()[i];
            }
        }
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        return rows[rowIndex][columnIndex];
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        rows[rowIndex][columnIndex] = aValue; 
        fireTableCellUpdated(rowIndex, columnIndex);
    }

    @Override
    public int getRowCount() {
        return rows.length;
    }

    @Override
    public int getColumnCount() {
        return columns.length;
    }

    @Override
    public Class getColumnClass(int column) {
        return getValueAt(0, column).getClass();
    }
}

public class OptionsTable extends JTable {

    private OPTIONS activeMainOption = OPTIONS.SHOW_SAILNUMBERS;
    private int rowActive = 0;

    public OptionsTable() {
        setModel(new OptionsTableModel());
        setDoubleBuffered(true);
        setVisible(true);
        setDefaultRenderer(Boolean.class, new JTableOptionsRenderer());
        setDefaultRenderer(OPTIONS.class, new JTableOptionsRenderer());
        setDefaultRenderer(String.class, new JTableOptionsRenderer());                

        setTableHeader(null);
        setGridColor(DefaultResources.getDefaultColor(DEFAULT_COLORSET.BORDER_COLOR));
        setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        setShowHorizontalLines(true);
        setIntercellSpacing(new Dimension(0, 1));

        getColumnModel().getColumn(0).setPreferredWidth(20);
        getColumnModel().getColumn(1).setPreferredWidth(120);

        setRowHeight(MenuContainer.lineHeight + MenuContainer.lineBorderThickness * 2);
        setRowHeight(6, 2); // Set the height for the empty Row, which is used for the seperator and does match with the NONE value of the OPTIONS Enumration

        this.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
                int row = rowAtPoint(e.getPoint());
                int column = columnAtPoint(e.getPoint());
                OPTIONS command = (OPTIONS) getModel().getValueAt(row, 1);

                if (getModel().getColumnClass(column) == Boolean.class) {
                    if (((Boolean) getModel().getValueAt(row, column)) == false) {

                        getModel().setValueAt(true, row, column);

                        if (command == OPTIONS.SHOW_PATH) {
                            SailingPainter.showPath = true;
                        } else if (command == OPTIONS.SHOW_TAIL) {
                            SailingPainter.showTails = true;
                        } else {
                            if (command != activeMainOption) {
                                getModel().setValueAt(false, rowActive, 0);
                            }
                            SailingPainter.option = command;
                            activeMainOption = command;
                            rowActive = row;
                        }

                    } else {

                        if (command == OPTIONS.SHOW_PATH) {
                            SailingPainter.showPath = false;
                        } else if (command == OPTIONS.SHOW_TAIL) {
                            SailingPainter.showTails = false;
                        } else {
                            SailingPainter.option = OPTIONS.NONE;
                        }

                        getModel().setValueAt(false, row, 0);
                    }
                    
                }                
            }
        });
    }
}
 
Keiner eine Idee? An sich wäre es mir möglich aus der Instanz der JTable heraus einen repaint auszuführen, dessen koordinaten mit der jeweiligen Cell matchen. Das ist aber irgendwie nicht Sinn der Sache. Das Ding ist, dass die fireTableCellUpdated(row, column) auch nur ein tableChanged ruft, dass wiederum die Listener des Models durchläuft und diesen die Veränderung mitteilt.

Müsste ich eventuell die Listener anpassen und überschreiben?!
 
wenn du von einem Thread oder Button aus setValueAt() aufrufst wird nur eine Zelle neu gemalt,
umgekehrt werden Zeilen beim Rumklicken in der Tabelle auch dann komplett neu gemalt wenn du dort gar nicht setValueAt() aufrufst,
einfach nur die Tabelle für sich sorgt für schöne Ansicht, weil sie z.B. mit Hervorhebung einer selektierten Zeile rechnet,
(edit: ok, so ganz passt das nicht, aber irgendwas in der Richtung)

wie sich das ausschalten läßt kann ich pauschal nicht sagen, ist das wirklich wichtig?
lass die Tabelle doch malen wie sie will, cache in deinem Model Daten falls dort ein Problem herrscht,

ach ja, es werden wirklich nur 1-2 Zeilen gemalt, nicht die ganze Tabelle oder wie kommst du dazu?
ein vollständiges Testprogramm zu posten wäre günstig, so ist dein Code unausführbar -> unnachprüfbar

hier eine abgespeckte lauffähige Variante

Java:
public class TestGUI
    extends JFrame
{
    public TestGUI()
        throws Exception
    {
        OptionsTable t = new OptionsTable();
        add(t);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setSize(400, 400);
        setVisible(true);

        OptionsTableModel m = (OptionsTableModel)t.getModel();
        for (int i = 0; i < 10; i++)
        {
            Thread.sleep(1000);
            m.setValueAt("k: " + i, 3, 0);
        }
    }


    public static void main(String[] args)
        throws Exception
    {
        new TestGUI();
    }

}


class JTableOptionsRenderer
    implements TableCellRenderer
{

    private JLabel textLabel = new JLabel();
    private JSeparator sep = new JSeparator(JSeparator.HORIZONTAL);

    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
                                                   int column)
    {
        System.out.println("paint: " + row + ", " + column + ", " + value);
        // new Error("paint: " + row + ", " + column + ", " + value).printStackTrace();
        textLabel.setText(value.toString());
        return textLabel;
    }
}

class OptionsTableModel
    extends AbstractTableModel
{

    private String[][] rows = new String[5][2];
    private String[] columns =
        {"checkbox", "Definition"};

    public OptionsTableModel()
    {
        initRows();
    }

    private void initRows()
    {
        for (int i = 0; i < rows.length; i++)
        {
            rows[i][0] = "";
            rows[i][1] = "";
        }
    }

    public Object getValueAt(int rowIndex, int columnIndex)
    {
        return rows[rowIndex][columnIndex];
    }

    public void setValueAt(Object aValue, int rowIndex, int columnIndex)
    {
        rows[rowIndex][columnIndex] = (String)aValue;
        fireTableCellUpdated(rowIndex, columnIndex);
    }

    public int getRowCount()
    {
        return rows.length;
    }

    public int getColumnCount()
    {
        return columns.length;
    }

    public Class getColumnClass(int column)
    {
        return getValueAt(0, column).getClass();
    }
}


class OptionsTable
    extends JTable
{
    private int rowActive = 0;

    public OptionsTable()
    {
        setModel(new OptionsTableModel());
        setDoubleBuffered(true);
        setDefaultRenderer(String.class, new JTableOptionsRenderer());

        setTableHeader(null);

        addMouseListener(new MouseAdapter()
            {

                int c = 0;

                public void mouseReleased(MouseEvent e)
                {
                    int row = rowAtPoint(e.getPoint());
                    int column = columnAtPoint(e.getPoint());

                    // getModel().setValueAt("" + c++, row, column);

                }
            });
    }
}
 
Zuletzt bearbeitet von einem Moderator:
Morgen !!

Ich habe dein Beispiel auch mal ausgeführt. Bei der Verwendung des Mouselisteners werden dabei nur die angrenzenden Zellen oder die jeweils ausgewählte neu gezeichnet. Aus einem mir bisher noch nicht bekannten Grund, wird bei mir aber jede Zelle neu gezeichnet.

Der Renderer durchläuft halt alle Zeilen und Spalten indezes. An sich wäre mir das für eine so einfache Tabelle egal, ob er jetzt alle oder nur eine Zelle zeichnet. Da ich aber eine weitaus umfangreichere Tabelle erst noch schreiben will, war das für mich bei einem einfachereren Beispiel jetzt halt der Aufhänger..
 
Kurze Rückmeldung -

wie es aussieht hat die Anweisung

setRowHeight(6, 2);

Quasi wollte ich ja in eine Zeile einen separator tricksen, der aber leider diesen Nebeneffekt verursacht.

LG
 
Ok ich habs hinbekommen - falls es jemanden interessiert. Habe jetzt den Separator aus dem TableModel entfernt und stattdessen die paintComponent der Table überschrieben.

Java:
@Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        
        paintSeparator(g);
    }
    private static final Stroke separator = new BasicStroke(3, BasicStroke.JOIN_ROUND, BasicStroke.JOIN_BEVEL);
    
    public void paintSeparator(Graphics g) {
        Graphics2D g2d = (Graphics2D) g.create();
        Rectangle rectOfSeparator = getCellRect(6, 0, true);
        
        int firstNonExistentRowY = rectOfSeparator.y; //the top Y-coordinate of the first empty tablerow

        int x = 0;
        for (int i = 0; i < getColumnCount(); i++) {
            TableColumn column = getColumnModel().getColumn(i);
            x += column.getWidth(); //add the column width to the x-coordinate                
        }
        g2d.setColor(Color.lightGray);
        g2d.setStroke(separator);
        g2d.drawLine(0, firstNonExistentRowY - 1, x, firstNonExistentRowY - 1);
        
        g2d.dispose();        
    }
 

Zurück
Oben