*Markus: JTable - Zelle automatisch selektieren

Hallo,

Ich habe in einer JTable 2 Spalten. Nun ist es so, dass nach der Eingabe von Daten in eine bestimmten Zelle nach dem Drücken der Enter-Taste die nächste selektierte Zelle automatisch jene unterhalb der gerade editierten ist. D.h., dass die Selektion automatisch in der selben Spalte bleibt, und eine Zeile weiter hinunter springt. Ich will es aber erreichen, dass der "Cursor" nach dem editieren eines Feldes in der ersten Spalte (und nach dem Enter-Drücken) in der selben Zeile bleibt, und die 2. Spalte in der Zeile, in der gerade die erste Spalte editiert wurde, automatisch ausgewählt wird. Nach dem Editieren der 2. Spalte soll der Cursor nun in die nächste Zeile springen, aber jetzt wieder in die erste Spalte, wodurch man jede Zelle nacheinander durch das Enter-Drücken durchgehen können soll.
Welche Klassen muss ich dafür verwenden? Ich weiß nämlich nicht wonach ich suchen soll, da ich bisher nicht fündig gewordne bin.

Markus

--
http://www.apostrophitis.at
六 7東曲 人港ラ
  1. Hallo,

    Ich habe in einer JTable 2 Spalten. Nun ist es so, dass nach der Eingabe von Daten in eine bestimmten Zelle nach dem Drücken der Enter-Taste die nächste selektierte Zelle automatisch jene unterhalb der gerade editierten ist. D.h., dass die Selektion automatisch in der selben Spalte bleibt, und eine Zeile weiter hinunter springt. Ich will es aber erreichen, dass der "Cursor" nach dem editieren eines Feldes in der ersten Spalte (und nach dem Enter-Drücken) in der selben Zeile bleibt, und die 2. Spalte in der Zeile, in der gerade die erste Spalte editiert wurde, automatisch ausgewählt wird. Nach dem Editieren der 2. Spalte soll der Cursor nun in die nächste Zeile springen, aber jetzt wieder in die erste Spalte, wodurch man jede Zelle nacheinander durch das Enter-Drücken durchgehen können soll.
    Welche Klassen muss ich dafür verwenden? Ich weiß nämlich nicht wonach ich suchen soll, da ich bisher nicht fündig gewordne bin.

    Jedes JComponent hat Keybindings, so auch JTable. Diese Keybindimgs arbeiten mit InputMaps und ActionMaps. Die Tatsache, dass bei [Enter] eine Zeile tiefer gegangen wird, resultiert daraus, dass dies in der InputMap [JTable].WHEN_ANCESTOR_OF_FOCUSED_COMPONENT so für [Enter] definiert ist. Diese Standarddefinition kannst Du verändern.

    Sei table ein JTable:

      
    table.getInputMap(JTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "selectNextColumnCell");  
    
    

    Was [InputMap].put(KeyStroke keyStroke, Object actionMapKey) und KeyStroke.getKeyStroke() machen, bekommst Du in der API-Doku heraus. Was der "actionMapKey" bei [InputMap].put(KeyStroke keyStroke, Object actionMapKey) ist, wird meines Wissens nach nirgends so recht beschrieben. Man kann sich eine Übersicht verschaffen mit:

      
    Object[] allTableActionMapKeys = table.getActionMap().allKeys();  
    for(int i = 0; i <  allTableActionMapKeys.length; i++) {  
       System.out.println(allTableActionMapKeys[i]);  
    }  
    
    

    viele Grüße

    Axel

    1. Hallo,

      danke, das hat wunderbar geklappt. Ich hätte allerdings noch eine weitere Frage, die auch gleich dazu passt:
      Ich habe für meine Tabelle ein eigenes Model geschrieben, das von DefaultTableModel erbt.

        
      import java.util.regex.Pattern;  
        
      import javax.swing.table.DefaultTableModel;  
        
      public class TelefonTableModel extends DefaultTableModel {  
        
       @Override  
       public void setValueAt(Object aValue, int row, int column)  {  
        String wert = aValue.toString().trim();  
        if (column == 0 && Pattern.matches("^\\+?\\d+?", wert) )  
            super.setValueAt(aValue, row, column);  
        else if (column == 1)  
            super.setValueAt(aValue, row, column);  
        else  
         super.setValueAt("", row, column);  
       }  
      }  
      
      

      Die überschriebene setValueAt-Methode wird richtig aufgerufen, nur weiß ich dummerweise nicht wo sie aufgerufen wird? In meiner GUI-Klasse implementiere ich den TableModelListener und die dazu gehörenden Methode

        
       public void tableChanged(TableModelEvent e) {  
        
       }  
      
      

      ..nur steht in dieser Methode der setValueAt gar nicht, aber dennoch werden die Daten gehalten und auch der reguläre Ausdruck wird richtig angewendet, was mich zu dem Schluss bringt, dass die Methode intern abgerufen wird.
      Mein Problem ist aber, dass ich bei falscher Eingabe eine Exception werfen will, aber wenn ich die Methode nicht "per Hand" aufrufe, wie kann ich dann die Exception fangen?

      Markus

      --
      http://www.apostrophitis.at
      六 7東曲 人港ラ
      1. Hallo,

        ..nur steht in dieser Methode der setValueAt gar nicht, aber dennoch werden die Daten gehalten und auch der reguläre Ausdruck wird richtig angewendet, was mich zu dem Schluss bringt, dass die Methode intern abgerufen wird.

        Ja und deshalb müsstest Du JTable erweitern, um hier so einzusteigen, wie Du Dir das vorstellst. Das würde ich aber nicht so machen.

        Mein Problem ist aber, dass ich bei falscher Eingabe eine Exception werfen will, aber wenn ich die Methode nicht "per Hand" aufrufe, wie kann ich dann die Exception fangen?

        Du willst den Nutzer bei falscher Eingabe informieren und verhindern, dass falsche Werte in der Tabellenzelle überhaupt stehen können? Dann würde ich den Weg über den CellEditor gehen. Etwa so:

          
                table.getColumn("Name der Spalte").setCellEditor(  
                  new DefaultCellEditor(new JTextField()) {  
                    public boolean stopCellEditing() {  
                      String wert = ((JTextField)getComponent()).getText().trim();  
                      if (Pattern.matches("^\\+?\\d+?", wert)) {  
                         return super.stopCellEditing();  
                      } else {  
                         System.out.println("Eingabeformat falsch!");  
                         return false;  
                      }  
                    }  
                  }  
                );  
        
        

        Falsche Eingaben werden nicht akzeptiert und man kommt erst aus dem Zelleditor-Modus, wenn man den Wert berichtigt hat.

        Hier ist ein Beispiel verlinkt, welches das etwas Benutzerfreundlicher regelt.

        viele Grüße

        Axel

        1. Hallo,

          Ja und deshalb müsstest Du JTable erweitern, um hier so einzusteigen, wie Du Dir das vorstellst. Das würde ich aber nicht so machen.

          Was genau meinst du mit "JTable erweitern"?
          Mittlerweile habe ich mir auch eine Lösung zusammengereimt und sie mit deiner verglichen. Jetzt bleibt die Frage offen, welche der beiden Lösungen die für dieses Beispiel bessere ist. Die Eingabe in dieser Tabelle wird an eine höhere Klasse, sozusagen die "Fachlogik-Klasse", weitergereicht, die beim falschen Einlesen in die ArrayList, die das "Endziel" der Eingabe ist, bereits auch schon eine IllegalArgumentException wirft, wodurch das nochmalige Überprüfen in der GUI eigentlich nicht notwendig scheint, da man ja sonst die gleiche Überprüfung zwei Mal macht.
          Ich habe meine Klasse im GUI folgendermaßen angepasst:

            
           public void tableChanged(TableModelEvent e) {  
            int row = e.getFirstRow();  
            int col = e.getColumn();  
            
            if (col == 0)   {  
               try   {  
                  pdfleser.telefonnrHinzufuegen(tablemodel.getValueAt(row, col).toString());  
               }  
               catch (IllegalArgumentException iae)  {  
               JOptionPane.showMessageDialog(this, iae);  
               }  
            }  
            
           }  
            
          
          

          (Die Verschönerung, dass der "Cursor" nicht ins nächste Feld springt, werde ich dann noch gleich hinzufügen)
          Mache ich es so, würde das sogar mein TelefonTableModel überflüssig machen, da ich dann nichts Spezielles mehr in dieser Klasse überprüfen müsste.
          Wäre diese Lösung besser, oder hätte ich die Sache mit deiner Lösung verwirklichen sollen.
          (Vielleicht treten ja später noch Umstände auf, die meine bisherige Lösung verkomplizieren)

          Markus

          --
          http://www.apostrophitis.at
          六 7東曲 人港ラ
          1. Hallo,

            Was genau meinst du mit "JTable erweitern"?

            So was ähnliches, wie Du untern machst, also nicht mit dem Standard JTable arbeiten, sondern dessen Methoden und ggf. sogar dessen Eigenschaften überschreiben und erweitern. Wie genau das zu tun wäre, damit man, wie Du wolltest, [TableModel].setValueAt() explizit im Progammcode aufrufen könnte, weiß ich allerdings auch nicht. Das sollte aber auch _irgendwie_ gehen. Es wäre aber, wie gesagt, meiner Meinung nach keiner weiteren Recherche wert, weil unsinnig ;-).

            Mittlerweile habe ich mir auch eine Lösung zusammengereimt und sie mit deiner verglichen. Jetzt bleibt die Frage offen, welche der beiden Lösungen die für dieses Beispiel bessere ist. Die Eingabe in dieser Tabelle wird an eine höhere Klasse, sozusagen die "Fachlogik-Klasse", weitergereicht, die beim falschen Einlesen in die ArrayList, die das "Endziel" der Eingabe ist, bereits auch schon eine IllegalArgumentException wirft, wodurch das nochmalige Überprüfen in der GUI eigentlich nicht notwendig scheint, da man ja sonst die gleiche Überprüfung zwei Mal macht.
            Ich habe meine Klasse im GUI folgendermaßen angepasst:

            public void tableChanged(TableModelEvent e) {
              int row = e.getFirstRow();
              int col = e.getColumn();

            if (col == 0)   {
                 try   {
                    pdfleser.telefonnrHinzufuegen(tablemodel.getValueAt(row, col).toString());
                 }
                 catch (IllegalArgumentException iae)  {
                 JOptionPane.showMessageDialog(this, iae);
                 }
              }

            }

            
            >   
            > (Die Verschönerung, dass der "Cursor" nicht ins nächste Feld springt, werde ich dann noch gleich hinzufügen)  
            
            Ja? Wie? Das stelle ich mir nicht so einfach vor. Wenn .tableChanged() involviert wird, ist der CellEditor schon fertig. Dann noch zu versuchen die Standard-Reaktion auf .stopCellEditing() noch zu verhindern oder gar wieder rückgängig zu machen, wäre wahrscheinlich kompliziert. Lasse mich aber gerne vom Gegenteil überzeugen ;-).  
              
            
            > Mache ich es so, würde das sogar mein TelefonTableModel überflüssig machen, da ich dann nichts Spezielles mehr in dieser Klasse überprüfen müsste.  
            
            Ja, wäre es mit meiner Methode auch.  
              
            
            > Wäre diese Lösung besser, oder hätte ich die Sache mit deiner Lösung verwirklichen sollen.  
            > (Vielleicht treten ja später noch Umstände auf, die meine bisherige Lösung verkomplizieren)  
              
            Von der Logik her würde ich die Inhaltsvalidierung von Zellinhalten im CellEditor vornehmen, Dort hast Du via  
              
            ((JComponent)getComponent())  
              
            zugriff auf das JComponent, welches die Zelle im Moment der Eingabe repräsentiert. In meinem Beispiel lese ich damit nur den Inhalt aus:  
              
            String wert = ((JTextField)getComponent()).getText().trim();  
              
            Du kannst aber mit dem JTextField auch alles Andere machen, was Du mit einem JTextField machen kannst. Bspw.: .setBackground(), .setForeground(), .selectAll(), .setCaretPosition() ...  
              
            viele Grüße  
              
            Axel
            
            1. Hallo,

              Ja? Wie? Das stelle ich mir nicht so einfach vor.

              Ich dachte an diese Keystroke-Actions.
              (http://www.informit.com/articles/article.asp?p=24130&seqNum=10&rl=1

              Aber zur Zeit habe ich, wie ich beim Herumprobieren draufkam, einige Logikprobleme im Programm, um die ich mich zuerst kümmern sollte.

              Markus

              --
              http://www.apostrophitis.at
              六 7東曲 人港ラ