2011-12-22 14 views
7

Uso un numero personalizzato TableCellRenderer con più JFormattedTextField nelle celle della tabella. Io uso lo stesso componente di TableCellEditor. Ora ho bisogno di sapere in cosa JFormattedTextField l'utente ha fatto clic, e anche dove in questo campo (può essere fatto con viewToModel).Come posso ottenere il componente nella posizione di clic del mouse, quando si utilizza un TableCellEditor?

Quando si utilizza un personalizzato TableCellEditor, l'unico modo per ottenere il Point dal clic del mouse è il metodo isCellEditable(EventObject e) in CellEditor. Il dato Point è nel sistema di coordinate dei genitori.

anEvent è nel sistema di coordinate del componente di richiamo.

Ma come posso ottenere il componente sulla coordinata cliccata? Ho provato con findComponentAt(Point p) ma restituisce null per me.

Ecco del codice Ho testato con:

@Override 
    public boolean isCellEditable(EventObject e) { 

     if(e instanceof MouseEvent) { 
      MouseEvent ev = (MouseEvent)e; 
      Point p = ev.getPoint(); 

      // gives strange values 
      Point p3 = editor.getLocation(); 

      // x: 0 y: 0 
      Point tp = ((JTable)e.getSource()).getLocation(); 

      // these returns null 
      Component c1 = renderer.findComponentAt(p); 
      Component c2 = editor.findComponentAt(p); 

      System.out.println("Click at " + p + " editor at: " + p3); 
     } 

     return true; 
    } 

I valori per la posizione del componente editor.getLocation(); dà valori quasi casuali per la coordinata y (ad esempio quando si utilizzano 5 righe della tabella).

Come posso ottenere il componente su cui l'utente ha fatto clic quando si utilizza uno TableCellEditor e uno TableCellRenderer?


Ecco un esempio completo:

public class FormattedTableEditDemo extends JFrame { 

    public FormattedTableEditDemo() { 

     MyTableModel model = new MyTableModel(); 
     MyTableCellEditorAndRenderer cellEditorAndRenderer = 
       new MyTableCellEditorAndRenderer();  

     JTable table = new JTable(model); 
     table.setDefaultRenderer(BigDecimal.class, cellEditorAndRenderer); 
     table.setDefaultEditor(BigDecimal.class, cellEditorAndRenderer); 
     table.setRowHeight(40); 

     add(new JScrollPane(table)); 
     pack(); 
     setDefaultCloseOperation(EXIT_ON_CLOSE); 
     setVisible(true); 

    } 

    class MyTableCellEditorAndRenderer extends AbstractCellEditor 
      implements TableCellEditor, TableCellRenderer { 

     MyCellPanel editor = new MyCellPanel(); 
     MyCellPanel renderer = new MyCellPanel(); 

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

     @Override 
     public Component getTableCellRendererComponent(JTable table, 
       Object value, boolean isSelected, boolean hasFocus, 
       int row, int column) { 
      renderer.setValue(value); 
      return renderer; 
     } 

     @Override 
     public Component getTableCellEditorComponent(JTable table, 
       Object value, boolean isSelected, int row, int column) { 
      editor.setValue(value); 
      return editor; 
     } 

     @Override 
     public boolean shouldSelectCell(EventObject e) { 
      return false; 
     } 

     @Override 
     public boolean isCellEditable(EventObject e) { 

      if(e instanceof MouseEvent) { 
       MouseEvent ev = (MouseEvent)e; 
       Point p = ev.getPoint(); 

       // gives strange values 
       Point p3 = editor.getLocation(); 

       // x: 0 y: 0 
       Point tp = ((JTable)e.getSource()).getLocation(); 

       // these returns null 
       Component c1 = renderer.findComponentAt(p); 
       Component c2 = editor.findComponentAt(p); 

       System.out.println("Click at " + p + " editor at: " + p3); 
      } 

      return true; 
     } 

    } 

    class MyCellPanel extends JPanel { 

     JFormattedTextField field1 = new JFormattedTextField(); 
     JFormattedTextField field2 = new JFormattedTextField(); 

     public MyCellPanel() { 

      field1.setColumns(8); 
      field2.setColumns(8); 

      field2.setValue(new BigDecimal("0.00")); 

      setLayout(new BorderLayout()); 
      add(field1, BorderLayout.WEST); 
      add(Box.createHorizontalStrut(30)); 
      add(field2, BorderLayout.EAST); 
     } 

     public Object getValue() { 
      return field1.getValue(); 
     } 

     public void setValue(Object value) { 
      field1.setValue(value); 
     } 
    } 

    class MyTableModel extends AbstractTableModel { 

     List<BigDecimal> values = new ArrayList<BigDecimal>(); 

     public MyTableModel() { 

      // test values 
      values.add(new BigDecimal("37.00")); 
      values.add(new BigDecimal("4305.90")); 
      values.add(new BigDecimal("386.04")); 
      values.add(new BigDecimal("3486.58")); 
      values.add(new BigDecimal("6546.45")); 
     } 

     @Override 
     public int getColumnCount() { 
      return 1; 
     } 

     @Override 
     public int getRowCount() { 
      return values.size(); 
     } 

     @Override 
     public Object getValueAt(int row, int column) { 
      return values.get(row); 
     } 

     @Override 
     public boolean isCellEditable(int row, int column) { 
      return true; 
     } 

     @Override 
     public Class<?> getColumnClass(int column) { 
      return BigDecimal.class; 
     } 

    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       new FormattedTableEditDemo(); 
      } 

     }); 
    } 

} 
+0

+1 per sscce, ma perché non ascoltare il campo desiderato stesso? – trashgod

+0

@trashgod: non riceve un 'MouseEvent' quando viene utilizzato in un' TableCellEditor' a meno che la cella non abbia già il focus. 'isCellEditable' è l'unico modo in cui posso ricevere' MouseEvent' quando clicco su una riga che non ha lo stato attivo. – Jonas

+0

No, voglio dire saltare il 'MouseEvent' del tutto e aggiungere un listener adatto (azione, proprietà, ecc.) A ciascun' JFormattedTextField'. – trashgod

risposta

10

Non del tutto sicuro di capire cosa sta andando male (appena fatemi sapere se sono fuori, così posso cancellare questo :-)

Supponendo che si desidera ottenere il componente "reale" sotto il mouse (click/premere) che ha attivato l'inizio della modifica, il trucco è quello di eseguire la conversione (dalle coordinate padre a quella dell'editor) dopo l' l'editor viene aggiunto al relativo genitore. Questo è garantito per shouldSelectCell, ma non per isCellEditable (quest'ultimo è chiamato prima)

A recent answer nel contesto di un albero (dovrebbe essere abbastanza simile) ha qualche esempio eseguibile. Ecco lo snippet pertinente:

/** 
* At this point in time the editing component is added to the table (not documented!) but 
* table's internal cleanup might not yet be ready 
*/ 
@Override 
public boolean shouldSelectCell(EventObject anEvent) { 
    if (anEvent instanceof MouseEvent) { 
     redirect((MouseEvent) anEvent); 
    } 
    return false; 
} 

private void redirect(final MouseEvent anEvent) { 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      MouseEvent ev = SwingUtilities.convertMouseEvent(anEvent.getComponent(), anEvent, editor); 
      // at this point you have the mouse coordinates in the editor's system 
      // do stuff, like f.i. findComponent 
      .... 
     } 
    }); 
} 
+1

+1 In retrospettiva, si può vedere che 'p3' stava mostrando la posizione _previous_. – trashgod

+0

@trashgod - buona osservazione, mancate che :-) – kleopatra

+0

@trashgod: ah, questo lo spiega. – Jonas

1

Questa non è una risposta alla tua domanda, ma una spiegazione del risultato si vede: findComponentAt() restituisce null perché "non v'è alcuna componente figlio al punto richiesto. " MyCellPanel si trova su un CellRendererPane utilizzato da JTable per velocizzare il rendering. C'è un esempio di come è usato here.

1

È possibile ottenere la riga e la colonna del tavolo dal punto cliccato. Quindi chiama lo stesso metodo getTableCellRendererComponent del renderer per ottenere il componente di rendering. Quindi correggere il punto sottraendo le altezze delle righe precedenti da y e le larghezze delle celle precedenti da x. Quindi ottenere il figlio corretto del componente reso.

+0

Ma come faccio a ottenere "figlio adatto del componente sottoposto a rendering"? Anche in questo caso non riesco a trovarlo con 'findComponentAt()'. – Jonas

+0

È possibile scrivere own findComponentAt. Prendi tutti i bambini e per ognuno di loro ottieni dei limiti e controlla se gli eventi x/y (corretti) sono nei limiti. – StanislavL