# JTable Float Column fuegt automatisch Punkt hinzu wenn man Zahlen eingibt.



## sirbender (23. Mrz 2009)

Hi,

Unten haengt der Code meiner JTable an. Mein Problem ist, dass wenn ich im Float column die erste Zelle selektiere und einfach Zahlen eingabe automatisch der '5' ein Dezimalpunkt folgt obwohl ich keinen eingebe. Also z.B.: 5.4323

Ich will dass man den Dezimalpunkt explizit eingeben muss um ihn zu erhalten und nicht diesen Automatismus.

Ich habe uebrigens getColumnClass(...) ueberschrieben um den Typ einer Column zurueckzugeben.

[HIGHLIGHT="Java"]
    	@Override
        public Class getColumnClass(int c) {
    		return columnTypes.get(c);
        }
[/HIGHLIGHT]


[HIGHLIGHT="Java"]
package components;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

public class SimpleTableDemo extends JPanel {
    private boolean DEBUG = false;

    public SimpleTableDemo() {
        super(new GridLayout(1,0));

        String[] columnNames = {"Integer Col", "Float Col"};

        Object[][] data = {
            {new Integer(1), new Float(5)},
            {new Integer(2), new Float(7.5)},
        };

        MyTableModel model = new MyTableModel(data, columnNames, new Class[]{Integer.class, Float.class});
        final JTable table = new JTable(model);
        table.setPreferredScrollableViewportSize(new Dimension(300, 100));
        table.setFillsViewportHeight(true);

        if (DEBUG) {
            table.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    printDebugData(table);
                }
            });
        }

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);
    }

    private void printDebugData(JTable table) {
        int numRows = table.getRowCount();
        int numCols = table.getColumnCount();
        javax.swing.table.TableModel model = table.getModel();

        System.out.println("Value of data: ");
        for (int i=0; i < numRows; i++) {
            System.out.print("    row " + i + ":");
            for (int j=0; j < numCols; j++) {
                System.out.print("  " + model.getValueAt(i, j));
            }
            System.out.println();
        }
        System.out.println("--------------------------");
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("SimpleTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        SimpleTableDemo newContentPane = new SimpleTableDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }


    class MyTableModel extends DefaultTableModel {
    	public Vector<Class> columnTypes;

        public MyTableModel(Object[][] data, String[] columnNames, Class[] columnTypes) {
        	super(data, columnNames);
        	this.columnTypes = new Vector<Class>(Arrays.asList(columnTypes));
		}

		/*
         * JTable uses this method to determine the default renderer/
         * editor for each cell.  If we didn't implement this method,
         * then the last column would contain text ("true"/"false"),
         * rather than a check box.
         */
    	@Override
        public Class getColumnClass(int c) {
//    		Class<?> clazz = getValueAt(0, c).getClass();
//    		System.out.println(clazz);
//			return clazz;
    		return columnTypes.get(c);
        }
    }

}

[/HIGHLIGHT]


----------



## Marco13 (23. Mrz 2009)

Falls die Stichworte "TableCellRenderer" und "NumberFormat" nicht weiterhelfen, sag' nochmal bescheid.


----------



## sirbender (23. Mrz 2009)

Marco13 hat gesagt.:


> Falls die Stichworte "TableCellRenderer" und "NumberFormat" nicht weiterhelfen, sag' nochmal bescheid.



Doch doch. Ich haette mir nicht soviel Muehe gemacht diese Minimalbeispiel von oben zu schreiben, wenn ich mit einen Experimenten erfolgreich gewesen waere.

Denke mal man muss beim DefaultCellEditor ansetzen, oder? Wenn es so einfach ist, kannst du bitte ein bissl ausfuehrlicher werden, oder den Beispielcode von oben modifizieren?


----------



## Ebenius (23. Mrz 2009)

In etwa so: [Highlight=Java]table.setDefaultEditor(Float.class, new DefaultCellEditor(
      new JFormattedTextField(new AbstractFormatterFactory() {

        @Override
        public AbstractFormatter getFormatter(JFormattedTextField tf) {
          return new NumberFormatter(new DecimalFormat("0."));
        }
      })) {

  {
    ((JFormattedTextField) editorComponent)
          .setHorizontalAlignment(SwingConstants.TRAILING);
  }

  @Override
  public Component getTableCellEditorComponent(
        JTable table,
        Object value,
        boolean isSelected,
        int row,
        int column) {
    super.getTableCellEditorComponent(table, value, isSelected, row,
          column);
    if (value instanceof Number) {
      ((JFormattedTextField) editorComponent).setValue(value);
    }
    return editorComponent;
  }

  @Override
  public Object getCellEditorValue() {
    return ((JFormattedTextField) editorComponent).getValue();
  }
});[/Highlight]
Ebenius


----------



## sirbender (23. Mrz 2009)

Danke fuer die Hilfe - aber die Float-Textfelder verhalten sich damit noch seltsamer. Neue Zahlen werden eingefuegt und die bestehende Zahl geloescht. Ausserdem werden die neuen Zahlen nicht ins TableModel geschrieben. Die Eingaben haben keinen Effekt.

Keine Kritik. Ich hab nur selbst schon ein Haufen Code innerhalb der letzten 3 Stunden zusammengeschwurbelt der aehnlich komische Effekte ergibt 

[HIGHLIGHT="Java"]
package components;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DecimalFormat;
import java.util.Arrays;
import java.util.Vector;

import javax.swing.DefaultCellEditor;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.JFormattedTextField.AbstractFormatter;
import javax.swing.JFormattedTextField.AbstractFormatterFactory;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.NumberFormatter;

public class SimpleTableDemo extends JPanel {
    private boolean DEBUG = false;

    public SimpleTableDemo() {
        super(new GridLayout(1,0));

        String[] columnNames = {"Integer Col", "Float Col"};

        Object[][] data = {
            {new Integer(5), new Float(5)},
            {new Integer(7), new Float(7.5)},
        };

        MyTableModel model = new MyTableModel(data, columnNames, new Class[]{Integer.class, Float.class});
        final JTable table = new JTable(model);


        table.setDefaultEditor(Float.class, new DefaultCellEditor(
				new JFormattedTextField(new AbstractFormatterFactory() {

					@Override
					public AbstractFormatter getFormatter(JFormattedTextField tf) {
						return new NumberFormatter(new DecimalFormat("0."));
					}
				})) {

			{
				((JFormattedTextField) editorComponent)
						.setHorizontalAlignment(SwingConstants.TRAILING);
			}

			@Override
			public Component getTableCellEditorComponent(JTable table,
					Object value, boolean isSelected, int row, int column) {
				super.getTableCellEditorComponent(table, value, isSelected,
						row, column);
				if (value instanceof Number) {
					((JFormattedTextField) editorComponent).setValue(value);
				}
				return editorComponent;
			}

			@Override
			public Object getCellEditorValue() {
				return ((JFormattedTextField) editorComponent).getValue();
			}
		});



        table.setPreferredScrollableViewportSize(new Dimension(300, 100));
        table.setFillsViewportHeight(true);

        if (DEBUG) {
            table.addMouseListener(new MouseAdapter() {
                public void mouseClicked(MouseEvent e) {
                    printDebugData(table);
                }
            });
        }

        //Create the scroll pane and add the table to it.
        JScrollPane scrollPane = new JScrollPane(table);

        //Add the scroll pane to this panel.
        add(scrollPane);
    }


    private void printDebugData(JTable table) {
        int numRows = table.getRowCount();
        int numCols = table.getColumnCount();
        javax.swing.table.TableModel model = table.getModel();

        System.out.println("Value of data: ");
        for (int i=0; i < numRows; i++) {
            System.out.print("    row " + i + ":");
            for (int j=0; j < numCols; j++) {
                System.out.print("  " + model.getValueAt(i, j));
            }
            System.out.println();
        }
        System.out.println("--------------------------");
    }

    /**
     * Create the GUI and show it.  For thread safety,
     * this method should be invoked from the
     * event-dispatching thread.
     */
    private static void createAndShowGUI() {
        //Create and set up the window.
        JFrame frame = new JFrame("SimpleTableDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        //Create and set up the content pane.
        SimpleTableDemo newContentPane = new SimpleTableDemo();
        newContentPane.setOpaque(true); //content panes must be opaque
        frame.setContentPane(newContentPane);

        //Display the window.
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        //Schedule a job for the event-dispatching thread:
        //creating and showing this application's GUI.
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGUI();
            }
        });
    }


    class MyTableModel extends DefaultTableModel {
    	public Vector<Class> columnTypes;

        public MyTableModel(Object[][] data, String[] columnNames, Class[] columnTypes) {
        	super(data, columnNames);
        	this.columnTypes = new Vector<Class>(Arrays.asList(columnTypes));
		}

		/*
         * JTable uses this method to determine the default renderer/
         * editor for each cell.  If we didn't implement this method,
         * then the last column would contain text ("true"/"false"),
         * rather than a check box.
         */
    	@Override
        public Class getColumnClass(int c) {
//    		Class<?> clazz = getValueAt(0, c).getClass();
//    		System.out.println(clazz);
//			return clazz;
    		return columnTypes.get(c);
        }
    }

}

[/HIGHLIGHT]


----------



## Ebenius (23. Mrz 2009)

Geht's so besser? [Highlight=Java]table.setDefaultEditor(Float.class, new DefaultCellEditor(
      new JFormattedTextField(new AbstractFormatterFactory() {

        @Override
        public AbstractFormatter getFormatter(JFormattedTextField tf) {
          final NumberFormat fmt = NumberFormat.getNumberInstance();
          fmt.setMinimumFractionDigits(0);
          fmt.setMaximumFractionDigits(3);
          return new NumberFormatter(fmt);
        }
      })) {

  {
    ((JFormattedTextField) editorComponent)
          .setHorizontalAlignment(SwingConstants.TRAILING);
  }

  @Override
  public Component getTableCellEditorComponent(
        JTable table,
        Object value,
        boolean isSelected,
        int row,
        int column) {
    super.getTableCellEditorComponent(table, value, isSelected, row,
          column);
    if (value instanceof Number) {
      ((JFormattedTextField) editorComponent).setValue(new Double(
            ((Number) value).doubleValue()));
    }
    return editorComponent;
  }

  @Override
  public Object getCellEditorValue() {
    final Object value =
          ((JFormattedTextField) editorComponent).getValue();
    return new Float(((Number) value).floatValue());
  }
});[/Highlight]
Ebenius


----------



## sirbender (23. Mrz 2009)

Nein. Das klappt alles nicht. Die Float Columns sollen sich verhalten wie Integer Columns beim editieren. Der einzige Unterschied ist, dass sie Dezimalzahlen als Eingabe zulassen sollen.

Ich bin grade dabei NumberEditor in JTable naeher zu betrachten.


----------



## Ebenius (23. Mrz 2009)

sirbender hat gesagt.:


> Nein. Das klappt alles nicht. Die Float Columns sollen sich verhalten wie Integer Columns beim editieren. Der einzige Unterschied ist, dass sie Dezimalzahlen als Eingabe zulassen sollen.


Das klappt schonmal ganz gut bei mir. Könntest Du etwas detaillierter beschreiben, was Dir nicht gefällt? :noe:

Ebenius


----------



## sirbender (23. Mrz 2009)

Ebenius hat gesagt.:


> Das klappt schonmal ganz gut bei mir. Könntest Du etwas detaillierter beschreiben, was Dir nicht gefällt? :noe:
> 
> Ebenius



Man kann Cells editieren indem man sie doppelt anklickt und Text eingibt...oder indem man mit dem Pfeiltasten hinnavigiert und einfach Text eingibt. Das geht zwar auch bei deinem Beispiel, aber wenn man die Cell verlaesst merkt sich die Cell deine Eingabe nicht - was sie aber eigentlich tuen sollte (ohne deinen zusaetzlichen Code).


----------



## Ebenius (23. Mrz 2009)

So - jetzt sollte aber alles so sein wie Du willst, oder? [Highlight=Java]final NumberFormat fmt = NumberFormat.getNumberInstance();
fmt.setMinimumFractionDigits(0);
fmt.setMaximumFractionDigits(3);

table.setDefaultEditor(Float.class, new DefaultCellEditor(
      new JFormattedTextField(fmt)) {

  {
    final JFormattedTextField ftf = (JFormattedTextField) editorComponent;
    ftf.setHorizontalAlignment(SwingConstants.TRAILING);
    ftf.removeActionListener(delegate);
    ftf.getActionMap().put("notify-field-accept", new AbstractAction() {

      public void actionPerformed(ActionEvent e) {
        delegate.actionPerformed(e);
      }
    });
  }

  @Override
  public Component getTableCellEditorComponent(
        JTable table,
        Object value,
        boolean isSelected,
        int row,
        int column) {
    editorComponent.setBorder(new LineBorder(Color.BLACK));
    super.getTableCellEditorComponent(table, value, isSelected, row,
          column);
    if (value instanceof Number) {
      value = new Double(((Number) value).doubleValue());
      ((JFormattedTextField) editorComponent).setValue(value);
    }

    return editorComponent;
  }

  @Override
  public Object getCellEditorValue() {
    final Object value =
          ((JFormattedTextField) editorComponent).getValue();
    return new Float(((Number) value).floatValue());
  }

  @Override
  public boolean stopCellEditing() {
    final JFormattedTextField ftf = (JFormattedTextField) editorComponent;
    boolean formatCorrect = true;
    try {
      ftf.commitEdit();
    } catch (ParseException ex) {
      formatCorrect = false;
    }

    if (formatCorrect) {
      final ParsePosition pos = new ParsePosition(0);
      final AbstractFormatter formatter = ftf.getFormatter();
      final Format fmt = ((NumberFormatter) formatter).getFormat();
      fmt.parseObject(ftf.getText(), pos);
      formatCorrect = pos.getIndex() == ftf.getText().length();
    }

    if (!formatCorrect) {
      editorComponent.setBorder(new LineBorder(Color.RED));
      return false;
    } else {
      return super.stopCellEditing();
    }
  }
});[/Highlight]
Ebenius


----------



## sirbender (23. Mrz 2009)

Das Loeschen von Ziffern erfolgt jetzt nicht von rechts nach links sondern umgekehrt.

Keine Angst den Rest des Weges schaff ich alleine 


DANKE fuer deine grossartige Hilfe. Du hast es total drauf wenn es um Swing geht


----------



## Ebenius (23. Mrz 2009)

sirbender hat gesagt.:


> Das Loeschen von Ziffern erfolgt jetzt nicht von rechts nach links sondern umgekehrt.


Nur so aus Interesse: Was löscht denn jetzt wie verkehrt herum. Hab das eben nochmal probiert und der Editor löscht wie ich es erwarten würde; genauso wie der Standard-Editor für Integer...

Ebenius


----------



## sirbender (23. Mrz 2009)

Ebenius hat gesagt.:


> Nur so aus Interesse: Was löscht denn jetzt wie verkehrt herum. Hab das eben nochmal probiert und der Editor löscht wie ich es erwarten würde; genauso wie der Standard-Editor für Integer...
> 
> Ebenius



Ich selektiere die Zellen nicht per Doppelklick sondern bewege die Selektion mit der Tastatur.

Wenn ich ueber einer Float-Zelle bin kann ich mit "Delete" loeschen, da der Cursor ganz links im Textfeld ist.

Normal ist der Cursor aber ganz rechts und man kann nur mit "Backspace" loeschen.


----------



## Ebenius (23. Mrz 2009)

Ahso. Herrje, das JFormattedTextField ist ganz schön zickig, was CaretPosition-Update angeht. So funktioniert:
[Highlight=Java]final NumberFormat fmt = NumberFormat.getNumberInstance();
fmt.setMinimumFractionDigits(0);
fmt.setMaximumFractionDigits(3);

final JFormattedTextField ftf =
      new JFormattedTextField(new NumberFormatter(fmt) {

        @Override
        public void install(JFormattedTextField ftf) {
          // unset the changed caret position from the formatter
          final int pos = ftf.getCaretPosition();
          super.install(ftf);
          ftf.setCaretPosition(Math.max(0, Math.min(ftf.getText()
                .length(), pos)));
        }
      });
table.setDefaultEditor(Float.class, new DefaultCellEditor(ftf) {

  {
    final JFormattedTextField ftf = (JFormattedTextField) editorComponent;
    ftf.setHorizontalAlignment(SwingConstants.TRAILING);
    ftf.removeActionListener(delegate);
    ftf.getActionMap().put("notify-field-accept", new AbstractAction() {

      public void actionPerformed(ActionEvent e) {
        delegate.actionPerformed(e);
      }
    });
  }

  @Override
  public Component getTableCellEditorComponent(
        JTable table,
        Object value,
        boolean isSelected,
        int row,
        int column) {
    editorComponent.setBorder(new LineBorder(Color.BLACK));
    super.getTableCellEditorComponent(table, value, isSelected, row,
          column);
    if (value instanceof Number) {
      value = new Double(((Number) value).doubleValue());
      final JFormattedTextField ftf =
            (JFormattedTextField) editorComponent;
      ftf.setValue(value);
      ftf.setCaretPosition(ftf.getText().length());
    }

    return editorComponent;
  }

  @Override
  public Object getCellEditorValue() {
    final Object value =
          ((JFormattedTextField) editorComponent).getValue();
    return new Float(((Number) value).floatValue());
  }

  @Override
  public boolean stopCellEditing() {
    final JFormattedTextField ftf = (JFormattedTextField) editorComponent;
    boolean formatCorrect = true;
    try {
      ftf.commitEdit();
    } catch (ParseException ex) {
      formatCorrect = false;
    }

    if (formatCorrect) {
      final ParsePosition pos = new ParsePosition(0);
      final AbstractFormatter formatter = ftf.getFormatter();
      final Format fmt = ((NumberFormatter) formatter).getFormat();
      fmt.parseObject(ftf.getText(), pos);
      formatCorrect = pos.getIndex() == ftf.getText().length();
    }

    if (!formatCorrect) {
      editorComponent.setBorder(new LineBorder(Color.RED));
      return false;
    } else {
      return super.stopCellEditing();
    }
  }
});[/Highlight]

Ebenius


----------



## sirbender (23. Mrz 2009)

So richtig kapiert wo mein Problem lag hab ich immer noch nicht. Kannst du mir das kurz erklaeren?

Haengt der Renderer immer diesen doofen Dezimalpunkt und die '0' dran und wird diese dann nur vom Editor korrekt angezeigt?


----------



## Ebenius (24. Mrz 2009)

sirbender hat gesagt.:


> So richtig kapiert wo mein Problem lag hab ich immer noch nicht. Kannst du mir das kurz erklaeren?


Der Renderer tut was Du möchtest, oder? Wenn nicht, müssen wir den auch noch anfassen. Der Standard-Editor der JTable für Double und Float ist leider 1. nicht visible (package-visible in JTable) und 2. nicht so konfiguriert wie Du ihn haben wolltest. Deshalb hab ich ihn nachgebaut.

Ist Dein Problem jetzt weg, oder besteht noch eines?

Ebenius


----------



## sirbender (24. Mrz 2009)

Ebenius hat gesagt.:


> Der Renderer tut was Du möchtest, oder? Wenn nicht, müssen wir den auch noch anfassen. Der Standard-Editor der JTable für Double und Float ist leider 1. nicht visible (package-visible in JTable) und 2. nicht so konfiguriert wie Du ihn haben wolltest. Deshalb hab ich ihn nachgebaut.
> 
> Ist Dein Problem jetzt weg, oder besteht noch eines?
> 
> Ebenius



Nein nein...Danke! Das Problem ist geloest. Nur irgendwie erkenne ich noch nicht ganz genau wer denn immer dieses doofe ".0" suffix im Editor an Floats angehaengt hat?


----------



## Ebenius (24. Mrz 2009)

Der in der JTable eingebaute Double-Editor nimmt das Standard NumberFormat. Und dieses formatiert halt immer 1.0 oder genauer.

Ebenius


----------



## sirbender (24. Mrz 2009)

Hi Ebenius,



Ebenius hat gesagt.:


> Der in der JTable eingebaute Double-Editor nimmt das Standard NumberFormat. Und dieses formatiert halt immer 1.0 oder genauer.
> 
> Ebenius



Ich seh irgendwie nirgends einen DoubleEditor. Ich sehe nur einen NumberEditor der nur ein normales JTextField enthaelt und welcher der von GenericEditor abgeleitet ist.

Es gibt einen DoubleRenderer - meinst du den?


----------



## Ebenius (24. Mrz 2009)

Oh weh, oh weh... Das hab ich ja komplett verwechselt. Es war der NumberEditor und der benutzt auch kein NumberFormat zum parsen, sondern new Double(String) und zum formatieren benutzt er Double.toString(). Und daher kommt das Problem. [Highlight=Java]System.out.println(new Double(1));[/Highlight]

```
1.0
```
Ebenius


----------



## sirbender (24. Mrz 2009)

Ebenius hat gesagt.:


> Oh weh, oh weh... Das hab ich ja komplett verwechselt. Es war der NumberEditor und der benutzt auch kein NumberFormat zum parsen, sondern new Double(String) und zum formatieren benutzt er Double.toString(). Und daher kommt das Problem. [Highlight=Java]System.out.println(new Double(1));[/Highlight]
> 
> ```
> 1.0
> ...



ja...aber das fuehrt auch nicht zu einer einfacheren Loesung, oder? 

trotzdem...irgendein Depp hat Generic- und NumberEditor package private gelassen. Ansonsten waer es glaube ich einfach zu loesen.


----------



## Ebenius (24. Mrz 2009)

sirbender hat gesagt.:


> ja...aber das fuehrt auch nicht zu einer einfacheren Loesung, oder?


Nö. Sollte nur die Antwort sein, warum es automatisch mit ".0" daherkommt.



sirbender hat gesagt.:


> trotzdem...irgendein Depp hat Generic- und NumberEditor package private gelassen. Ansonsten waer es glaube ich einfach zu loesen.


Sehe ich genauso. Wahrscheinlich ist die Lösung nicht "schön" genug, um sie freizugeben; schließlich arbeitet das ganze Ding mit Reflection.

Ebenius


----------

