sintaxis - setmodel java
JTable con un editor complejo (5)
Vea algunos artículos relacionados aquí y aquí .
Otro buen artículo sobre la edición de JTable en general.
Tengo muchos editores personalizados para una JTable y es un eufemismo decir que la usabilidad, especialmente en lo que respecta a la edición con el teclado, es deficiente.
La razón principal de esto es que mis editores siempre se crean con una situación similar (aunque a menudo más compleja) a esto:
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
JPanel container = new JPanel();
container.setLayout(new BorderLayout());
container.add(field, BorderLayout.CENTER);
field.setText((String) value);
container.add(new JButton("..."), BorderLayout.EAST);
return container;
}
IE un panel con más de un componente adentro. El editor de texto real es un descendiente del componente que se devuelve como editor. Entonces, dejando de lado los problemas de renderización, por lo que puedo decir, JTable está enfocando el componente que devuelve el método getTableCellEditorComponent
, de modo que cuando presiona una tecla con una celda resaltada pasa el foco y la tecla presiona el panel, pensando que es el editor .
¿Hay alguna forma en que pueda informar a JTable que el editor "real" es el JTextfield? Agregar una requestFocusInWindow
en el componente correcto es insuficiente ya que la pulsación de tecla no se transmitirá.
Arreglé algo similar en 2 pasos
Primero anule el editCellAt de su JTable y llame requestFocus después de preparar el editor:
public boolean editCellAt( int row, int column, EventObject e )
{
if ( cellEditor != null && !cellEditor.stopCellEditing() )
{
return false;
}
if ( row < 0 || row >= getRowCount() ||
column < 0 || column >= getColumnCount() )
{
return false;
}
if ( !isCellEditable(row, column) )
return false;
TableCellEditor editor=getCellEditor(row, column);
if ( editor != null && editor.isCellEditable(e) )
{
editorComp=prepareEditor(editor, row, column);
if ( editorComp == null )
{
removeEditor();
return false;
}
//aangepast
Rectangle rect=getCellRect(row, column, false);
if ( datamodel_.useAdaptedEditorRect() )
rect=datamodel_.changeRectangle(rect, editorComp);
editorComp.setBounds(rect);
add(editorComp);
editorComp.validate();
setCellEditor(editor);
setEditingRow(row);
setEditingColumn(column);
editor.addCellEditorListener(this);
//NEXT LINE ADDED
editorComp.requestFocus();
return true;
}
return false;
}
A continuación, sobrecargue RequestFocus desde su JPanel y asegúrese de que su campo de texto se ponga como editorComponent:
public class EditorPanel extends JPanel {
JComponent editorComponent;
public boolean isRequestFocusEnabled()
{
return true;
}
public void requestFocus()
{
editorComponent.requestFocus();
}
}
Siempre puedes tomar keyEvent y configurarlo tú mismo:
AWTEvent event = EventQueue.getCurrentEvent();
if ( event instanceof KeyEvent )
{
char newSelection = ( (KeyEvent) event).getKeyChar();
int keyCode = ( (KeyEvent) event ).getKeyCode();
editorComponent.requestFocus();
if ( editorComponent instanceof JTextField )
{
if ( ( newSelection >= (char) FIRST_ALLOWED_CHAR ) && ( newSelection != (char) LAST_ALLOWED_CHAR ) ) //comes from DefaultKeyTypedAction
( (JTextField) editorComponent ).setText(Character.toString(newSelection));
if ( keyCode == KeyEvent.VK_BACK_SPACE || keyCode == KeyEvent.VK_DELETE )
( (JTextField) editorComponent ).setText("");
}
}
else
editorComponent.requestFocus();
Creo que lo resolví
Para decirte la verdad, no sé qué solucionó el problema, ya que estoy usando un editor personalizado, un procesador personalizado y esas cosas ...
Cuando se resalta una celda y presiono "abc", las 3 letras van a la pantalla (celda, en este caso).
grid.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent ke) {
int l = grid.getSelectedRow();
int c = grid.getSelectedColumn();
grid.editCellAt(l, c);
}
});
Bueno ... lo intenté ... =)
(No sé si es lo mismo porque mi JTable usa JTextField y JComboBox como editores).
Si leo su pregunta correctamente, quiere que el usuario pueda escribir en una celda inmediatamente, sin activar primero el editor de celda, es decir, quiere que la pulsación de tecla activada la celda sea el primer carácter ingresado en el campo de texto.
Mi primer intento fue agregar un propertyChangeListener en la propiedad focusOwner del KeyboardFocusManager, solo para notar que el foco nunca abandona el JTable. Probablemente te encontraste con eso también. Tiempo para el plan B.
Hice que funcionara la "primera pulsación de tecla" añadiendo un KeyListener a la tabla que registra el último KeyEvent para el método keyPressed () en un campo de instancia. El método getTableCellEditorComponent () lee el carácter desde allí. También necesitaba esa llamada hacky requestFocusInWindow () que mencionas si el usuario debe seguir escribiendo cualquier carácter después del primero.
Para mi aplicación de muestra, creé una subclase de JTable que agrega un KeyListener a sí mismo. Es una idea mucho mejor hacer que su instancia de CellEditor implemente KeyListener y agregarla a la JTable normal, pero se lo dejo a usted.
Aquí está su fragmento de código cuando lo modifiqué:
@Override
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
JPanel container = new JPanel();
container.setLayout(new BorderLayout());
container.add(field, BorderLayout.CENTER);
// Will want to add an instanceof check as well as a check on Character.isLetterOrDigit(char).
char keypressed = ((StickyKeypressTable)table).getLastKeyPressed();
field.setText(String.valueOf(keypressed));
container.add(new JButton("..."), BorderLayout.EAST);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// This needs to be in an invokeLater() to work properly
field.requestFocusInWindow();
}
});
return container;
}
En lo que respecta a la maldad, esto se encuentra en algún lugar con Vogon Poetry, pero debería resolver su problema inmediato.
Tuve un problema muy similar. En mi caso, tenía TableCellEditor
complejo que consta de JSpinner y algunos otros componentes. El problema fue que cuando el editor celular comenzó, quería transferir el foco a su componente interno. Lo arreglé llamando a panel.transferFocusDownCycle()
pero esto a su vez causó que los eventos de teclado dejaran de funcionar; cuando mi componente interno tenía foco y presioné la tecla hacia arriba, esperaba que ese componente interceptara este evento y cambiara su valor. En su lugar, la tabla cambió el foco de fila al anterior ... Lo solucioné añadiendo KeyListener
y enviando todos los eventos clave directamente al componente interno.
Esta es la clase contenedora basada en JPanel
que escribí para hacer mi vida más fácil.
public class ContainerPanel extends JPanel implements KeyListener, FocusListener {
private JComponent component = null;
public ContainerPanel(JComponent component) {
this.component = component;
addKeyListener(this);
addFocusListener(this);
setFocusCycleRoot(true);
setFocusTraversalPolicy(new ContainerOrderFocusTraversalPolicy());
add(component);
}
@Override
public void keyTyped(KeyEvent e) {
component.dispatchEvent(e);
}
@Override
public void keyPressed(KeyEvent e) {
component.dispatchEvent(e);
}
@Override
public void keyReleased(KeyEvent e) {
component.dispatchEvent(e);
}
@Override
public void focusGained(FocusEvent e) {
component.transferFocusDownCycle();
}
@Override
public void focusLost(FocusEvent e) {
}
}