2011-12-22 18 views
7

Uso un TableCellRenderer personalizado con JFormattedTextField múltiple en las celdas de la tabla. Uso el mismo componente que TableCellEditor. Ahora necesito saber en qué JFormattedTextField hizo clic el usuario, y también dónde en este campo (se puede hacer con viewToModel).¿Cómo puedo obtener el componente en la posición de clic del mouse, cuando uso un TableCellEditor?

Cuando se utiliza una costumbre TableCellEditor, la única manera de conseguir el Point desde el clic del ratón es el método isCellEditable(EventObject e) en CellEditor. El Point dado está en el sistema de coordenadas de los padres.

anEvent está en el sistema de coordenadas del componente que invoca.

Pero, ¿cómo puedo obtener el componente en la coordenada cliqueada? Lo he intentado con findComponentAt(Point p) pero devuelve null para mí.

Aquí hay un código He probado 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; 
    } 

Los valores para la ubicación del componente editor.getLocation(); da valores casi aleatorios para la coordenada (por ejemplo, cuando se utiliza 5 filas de la tabla).

¿Cómo puedo obtener el componente que el usuario hizo clic cuando usa un TableCellEditor y un TableCellRenderer?


Aquí es un ejemplo 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 para sscce, pero ¿por qué no escuchar el campo deseado? – trashgod

+0

@trashgod: No recibe 'MouseEvent' cuando se usa en' TableCellEditor' a menos que la celda ya tenga foco. 'isCellEditable' es la única forma en que puedo recibir el' MouseEvent' al hacer clic en una fila que no tiene foco. – Jonas

+0

No, me refiero a omitir el 'MouseEvent' por completo y agregar un oyente adecuado (acción, propiedad, etc.) a cada' JFormattedTextField'. – trashgod

Respuesta

10

No del todo seguro de entender lo que va mal (sólo házmelo saber si estoy fuera, así que puede eliminar este :-)

Suponiendo que desea obtener el componente "real" en el marco del ratón (clic/prensa) que activó el inicio de la edición, el truco es hacer la conversión (de las coordenadas padre a editor) después de el editor se agrega a su padre. Eso está garantizado para shouldSelectCell, pero no para isCellEditable (siendo esta última llamada antes de )

Un recent answer en el contexto de un árbol (debería ser suficiente) posee ciertas ejemplo ejecutable. Aquí está el fragmento relevante:

/** 
* 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 En retrospectiva, se puede ver que 'p3' mostraba la posición _previous_. – trashgod

+0

@trashgod - buena observación, se perdió eso :-) – kleopatra

+0

@trashgod: ah, eso lo explica. – Jonas

1

Ésta no es una respuesta a su pregunta, pero una explicación del resultado que ver: findComponentAt() vuelve null porque "no hay ningún componente secundario en el punto solicitado ". MyCellPanel se encuentra en un CellRendererPane utilizado por JTable para acelerar la representación. Hay un ejemplo de cómo se usa here.

1

Puede obtener la fila y la columna de la tabla desde el punto seleccionado. A continuación, llame al método getTableCellRendererComponent del mismo renderizador para obtener el componente de renderizador. Luego corrija el punto restando las alturas de las filas previas de yy el ancho de las celdas previas de x. Luego obtenga el hijo apropiado del componente renderizado.

+0

¿Pero cómo obtengo el "hijo apropiado del componente renderizado"? No puedo obtenerlo con 'findComponentAt()' en este caso tampoco. – Jonas

+0

Puedes escribir tu propio findComponentAt. Obtenga todos los hijos y para cada uno de ellos obtenga límites y verifique si los eventos x/y (corregidos) están dentro de los límites. – StanislavL

Cuestiones relacionadas