使用复杂编辑器的JTable

8

我有很多自定义编辑器用于JTable,可以说在使用键盘进行编辑时,可用性不足是一种轻描淡写的表述。

主要原因是我的编辑器总是被创建为类似于这种情况(尽管通常更复杂):

@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;
}

即一个包含多个组件的面板。实际的文本编辑器是作为返回的组件的子孙元素。 因此,除了渲染问题之外,从我所了解的情况来看,JTable正在聚焦由getTableCellEditorComponent方法返回的组件,因此当您按下突出显示的单元格中的键时,它会将焦点和按键传递给面板,认为那是编辑器。
有没有办法告诉JTable,“真正”的编辑器是JTextField? 在正确的组件上添加一个hacky的requestFocusInWindow是不够的,因为按键不会被传递。

4个回答

2
如果我理解你的问题正确,你希望用户能够立即输入单元格,而不需要先激活单元格编辑器,也就是说,你希望激活单元格的任何按键都成为文本字段中输入的第一个字符。
我的第一次尝试是在KeyboardFocusManager的focusOwner属性上添加propertyChangeListener,但注意到焦点从未离开JTable。你可能也遇到了这个问题。那么就要进行计划B了。
我通过向表格添加KeyListener来使"第一次按键"的功能起作用,并将最后一个KeyEvent记录在实例字段中的keyPressed()方法中。getTableCellEditorComponent()方法从那里读取字符。如果用户想在第一个字符之后继续打字,我还需要使用requestFocusInWindow()调用该hacky(笨拙的)方法。
对于我的示例应用程序,我创建了一个JTable的子类,向其中添加了一个KeyListener。更好的做法是让您的CellEditor实例实现KeyListener并将其添加到常规JTable中,但我会留给您去完成。
以下是我修改后的代码片段:
@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;
}

就恶心程度而言,这与沃冈诗歌差不多,但它可以解决您的即时问题。


感谢您的回答。我们已经在不同的编辑器中以不同的方式有效地执行了这个操作一段时间了,但这会给我们带来各种麻烦(例如,编辑器由鼠标激活,而表键监听器仍然有一个lastKeyPressed)。我们正在寻找更好的解决方案。 - Tom Martin
我进一步研究了一下,最终到达了<code>BasicTableUI</code>中的<code>Handler</code>内部类。不过还没有代码示例。 - Barend

0

我曾经遇到了非常类似的问题。在我的情况下,我有一个复杂的 TableCellEditor,其中包含 JSpinner 和其他一些组件。问题是当单元格编辑器开始时,我想将焦点转移到其内部组件上。我通过调用 panel.transferFocusDownCycle() 解决了这个问题,但这反过来导致键盘事件停止工作——当我的内部组件获得焦点并按键向上时,我期望该组件将拦截此事件并更改其值。相反,表格会将行焦点改变到上面的一行... 我通过添加 KeyListener 并直接将所有键事件分派到内部组件中来解决了这个问题。

这是我编写的基于 JPanel 的包装器类,让我的生活更容易。

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) {
    }
}

0

我用两个步骤解决了类似的问题

首先,覆盖你的JTable中的editCellAt方法,并在准备编辑器后调用requestFocus:

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;
}

然后从您的JPanel中重载requestFocus,并确保将您的文本字段放置为editorComponent:

public class EditorPanel extends JPanel {
   JComponent editorComponent;

   public boolean isRequestFocusEnabled()
   {
     return true;
   }

   public void requestFocus()
   {
   editorComponent.requestFocus();
   }
}

您可以随时获取keyEvent并自行设置:

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();

是的,我以前尝试过这种方法。我在我的代码上尝试了一下,正如我所预料的那样,激活编辑器的第一个按键没有被传递(就像使用简单编辑字段时那样),也就是说,如果你切换到单元格并按下“123”,组件中只会出现“23”。 - Tom Martin

0

我想我解决了它。
说实话,我不知道是什么解决了这个问题,因为我使用的是自定义编辑器、自定义渲染器等等...

当一个单元格被高亮显示并且我按下“abc”时,这三个字母会出现在屏幕上(在这种情况下是单元格)。

grid.addKeyListener(new KeyAdapter() {
    public void keyTyped(KeyEvent ke) {
        int l = grid.getSelectedRow();
        int c = grid.getSelectedColumn();
        grid.editCellAt(l, c);
    }
});

唔...我试过咯... =)
(唔知道系咪一样,因为我用 JTable 时使用 JTextField 和 JComboBox 作为编辑器)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接