# JSpinner als TableCellEditor; buttons nur bei fokus sichtbar



## mrks.js (4. Feb 2010)

Hallo, ich habe mir einen TableCellEditor zusammengeklaut, der aus einer meiner Tabellenspalten statt dem normalen textfeld einen jspinner macht. das funktioniert soweit auch, nur werden die previous/next-buttons des spinners erst beim fokus auf das feld sichtbar gesetzt. ich habe die anforderung, dass die buttons (so wie bei jedem spinner) immer sichtbar sind, ob die zelle den fokus hat oder nicht.


```
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class Spinner {
    public static void main(String[] args) {
        JTable table = new JTable(2, 1);

        // Set up the table
        table.getColumnModel().getColumn(0).setCellEditor(new
                NumberSpinnerTableCellEditor());
        
        table.getModel().setValueAt(new Integer(0), 0, 0);
        table.getModel().setValueAt(new Integer(0), 1, 0);
        JScrollPane scrollPane = new JScrollPane(table);

        // Put the areas on one panel
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.add(scrollPane, BorderLayout.CENTER);

        // Show the frame
        JFrame frame = new JFrame("Spinner Editor Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setVisible(true);
    }

    // The "Spinner" editor for the table.
    private static class NumberSpinnerTableCellEditor extends
            AbstractCellEditor implements TableCellEditor {

        private JSpinner editor;

        public NumberSpinnerTableCellEditor() {
            editor = new JSpinner();
            editor.setBorder(null);
        }

        public Object getCellEditorValue() {
            return editor.getValue();
        }

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

            editor.setValue(value);

            return editor;
        }
    }
 
}
```


----------



## Michael... (4. Feb 2010)

mrks.js hat gesagt.:


> ich habe die anforderung, dass die buttons (so wie bei jedem spinner) immer sichtbar sind, ob die zelle den fokus hat oder nicht.


Dann musst Du zusätzlichen einen JSpinner als Rendererkomponente verwenden.


----------



## mrks.js (4. Feb 2010)

vielen dank für deine rückmeldung!

ich habe nun

```
private static class NumberSpinnerTableCellRenderer extends DefaultTableCellRenderer {
    	public NumberSpinnerTableCellRenderer() {
    		super();
    	}
    }
```

und 


```
table.getColumnModel().getColumn(0).setCellRenderer(new NumberSpinnerTableCellRenderer());
```

hinzugefügt. jedoch ändert das nichts an der darstellung. ich nehme mal stark an, dass ich noch setValue(Object value) überschreiben muss? aber wohin schreibe ich den wert? ich kann ja nicht nochmal ein jspinner objekt in meiner renderer methode hinzufügen? wäre ja redundant?!


----------



## Michael... (4. Feb 2010)

Du musst genauso wie beim Editor einen JSpinner eben als Renderer erzeugen und die 
	
	
	
	





```
getTableCellRendererComponent()
```
 des DefaultTableCellRenderer überschreiben.

so in etwa:

```
private static class NumberSpinnerTableCellRenderer extends DefaultTableCellRenderer {
        JSpinner renderer;
        public NumberSpinnerTableCellRenderer() {
            renderer = new JSpinner();
        }
        
        public Component getTableCellRendererComponent(... {
            renderer.setValue(value);
            return renderer;
        }
}
```


----------



## mrks.js (4. Feb 2010)

super! ich danke dir!
lerne jeden tag was zu swing dazu...

also muss ich auf rendern und auf edits reagieren und jedesmal wird mir der aktualisierte spinner zurückgegeben.


hier nochmal der komplette code:


```
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class Spinner {
    public static void main(String[] args) {
    	
        JTable table = new JTable(2, 1);

        // Set up the table
        table.getColumnModel().getColumn(0).setCellRenderer(new NumberSpinnerTableCellRenderer());
        table.getColumnModel().getColumn(0).setCellEditor(new
                NumberSpinnerTableCellEditor());
        
        table.getModel().setValueAt(new Integer(0), 0, 0);
        table.getModel().setValueAt(new Integer(0), 1, 0);
        JScrollPane scrollPane = new JScrollPane(table);

        // Put the areas on one panel
        JPanel contentPane = new JPanel(new BorderLayout());
        contentPane.add(scrollPane, BorderLayout.CENTER);

        // Show the frame
        JFrame frame = new JFrame("Spinner Editor Test");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);
        frame.pack();
        frame.setVisible(true);
    }

    // The "Spinner" editor for the table.
    private static class NumberSpinnerTableCellEditor extends
            AbstractCellEditor implements TableCellEditor {

        private JSpinner editor;

        public NumberSpinnerTableCellEditor() {
        	
            editor = new JSpinner();
            editor.setBorder(null);
        }

        public Object getCellEditorValue() {
        	
            return editor.getValue();
        }

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

            editor.setValue(value);
            return editor;
        }
    }
    private static class NumberSpinnerTableCellRenderer extends DefaultTableCellRenderer {
    	
    	JSpinner renderer;
    	public NumberSpinnerTableCellRenderer() {
    		renderer = new JSpinner();
    		renderer.setBorder(null);
    	}
    	
    	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {

    		renderer.setValue(value);
            return renderer;
        }
    }

}
```


----------



## mrks.js (23. Feb 2010)

Ich nochmal:

mir ist mittlerweile aufgefallen, dass sich die vom spinner veränderbaren werte erst durch einen mausklick auf ein anderes feld in der tabelle verändern. wo müsste ich ansetzen, dass mit jedem klick des spinner mehr/weniger buttons das attribut dahinter geändert wird? ich nehme mal an da muss ich händisch irgendwo ein firetablefireTableDataChanged() hinzufügen, nur wo?


----------



## Michael... (23. Feb 2010)

mrks.js hat gesagt.:


> mir ist mittlerweile aufgefallen, dass die vom spinner veränderbaren werte erst durch einen mausklick auf ein anderes feld in der tabelle verändern.


Korrekt der Wert des Editors wird erst nach dessen Beenden an das TableModel übergeben.
Wenn die Werte bereits während dem hin und her ziehen ins Datenmodel der Tabelle sollen, müsste man einen Listener (weiss jetzt aus dem stegreif nicht welchen) an dem Spinner registrieren und über diesen dann das Datenmodel aktualisieren, z.B. mit setValueAt(...)


----------



## mrks.js (23. Feb 2010)

das heisst ich müsste mir ein spinnermodel implementieren (das habe ich bereits gemacht) und in dem fall wo die getNextValue() bzw getPreviousValue() methode aufgerufen wird ein update der tabelle abfeuern? wie greife ich jedoch auf die update methode der anderen komponente zu? die ist ja nicht ohne objekt erreichbar.


----------



## Michael... (23. Feb 2010)

mrks.js hat gesagt.:


> das heisst ich müsste mir ein spinnermodel implementieren (das habe ich bereits gemacht) und in dem fall wo die getNextValue() bzw getPreviousValue() methode aufgerufen wird ein update der tabelle abfeuern?


Ob da das Spinnermodel der geeignete Platz ist - würde eher sagen nein.
Ausserdem musst Du nicht ein update ala fireTable... feuern, sondern Du musst aktiv das Datenmodel der Tabelle ändern (solange die den Schieber hin und herschiebst, ändert sich noch nichts an den Daten in der Tabelle) sofern das Datenmodel richtig implementiert ist, feuert dieses von alleine das Event ab.

Ich würde innerhalb des CellEditors einfach einen ChangeListener an den JSpinner anhängen, schließlich muss man sich ja auch merken an welcher Zelle der Editor gestartet wurde.


----------



## mrks.js (23. Feb 2010)

das hört sich grade wieder zu astronautisch an, ich werde das morgen einmal ausprobieren, wenn mein kopf wieder funktionert.
danke schonmal für Deine rückmeldung!


----------



## mrks.js (24. Feb 2010)

so. jetzt hab ich mir einen changelistener geschrieben der den wert des spinners ausspuckt. wie bekomme ich den jetzt in meine tabelle? ich sehe keine möglichkeit an die herkunft des spinners heranzukommen damit ich gezielt eine zelle updaten könnte!


----------



## mrks.js (24. Feb 2010)

jetzt hab ichs! glaub ich..

ich musste allerdings die tabelle aus meiner gui klasse sichtbar machen, das ist nicht so schön, aber hier gilt wohl "design" follows function..

mittels table.getEditingColumn() Reporter.table.getEditingRow() kann ich die aktuelle spalte und reihe abfragen von der das event gefeuert wurde, mit diesen informationen und dem wert aus dem spinner kann ich den wert dann per table.setValueAt(...) setzen.

ich danke Dir schon wieder!

für wissbegierige:


```
import javax.swing.AbstractSpinnerModel;
import javax.swing.JSpinner;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class SpinnerChangeListener implements ChangeListener {

	JSpinner source;
	AbstractSpinnerModel model;
	Object data;
	double value;
	
	public void stateChanged(ChangeEvent e) {

		if (Reporter.table.getEditingColumn() >= 0
				&& Reporter.table.getEditingRow() >= 0) {

			source = (JSpinner) e.getSource();
			model = (AbstractSpinnerModel) source
					.getModel();
			data = model.getValue();
			value = Double.parseDouble(data.toString());

			System.out.println("new value " + value);

			Reporter.table.setValueAt(data, Reporter.table.getEditingRow(),
					Reporter.table.getEditingColumn());

		}
	}
}
```


----------



## Michael... (24. Feb 2010)

Wenn der Listener den CellEditor kennt oder eine innere Klasse der CellEditors ist kannst Du Dir ja bei jedem Aufruf von getTableCellEditorComponent(JTable *table*, ..., int *row*, int *column*) die Referenz der Tabelle, die Reihe und die Spalte in einer globalen Instanzvariablen speichern, kannst vom Listener aus darauf zugreifen und ersparst Dir diese Abhängigkeiten. 

"design" follows function ist in den wenigsten Fällen eine die bessere Wahl ;-)


----------



## mrks.js (24. Feb 2010)

stimmt, das ist definitiv eleganter! 
werde ich nach meiner mittagspause implementieren. 
vielen Dank nochmal für Deine Mühe!
darf man fragen wie lange Du dich schon mit java und mit oberflächen beschäftigst? beruflich?


----------



## Michael... (24. Feb 2010)

mrks.js hat gesagt.:


> darf man fragen wie lange Du dich schon mit java und mit oberflächen beschäftigst?


Java kenne ich seit '98 und hatte seit dem mehr oder weniger mit zu tun. Mit kleineren Oberflächen hat man ja immer wieder mal zu tun, so richtig setzte ich mich seit 2-3 Jahren damit auseinander.


mrks.js hat gesagt.:


> beruflich?


Entwickle zwar kleinere Anwendungen und Frontends für Datenbanken, werde aber offiziell nicht dafür bezahlt. Offizielle Software wird bei uns von externen Firmen entwickelt. Ein Tochterunternehmen, welches früher Software für uns entwickelt hat wurde leider vor zwei Jahren verkauft, daher beschäftige ich mich eher mit Einkaufen und Beauftragen von Softwareentwicklungen.


----------

