JTable со сложным редактором

у меня есть много пользовательских редакторов для 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 на правильном компоненте недостаточно, так как нажатие клавиши не будет передано.

5 ответов


проверьте некоторые связанные статьи здесь и здесь.

другое хорошая статья о редактировании JTable в целом.


Если я правильно прочитал Ваш вопрос, вы хотите, чтобы пользователь мог сразу ввести в ячейку, не активируя сначала редактор ячеек, т. е. вы хотите, чтобы любое нажатие клавиши активировало ячейку как первый символ, введенный в текстовое поле.

моей первой попыткой было добавить propertyChangeListener в свойство focusOwner KeyboardFocusManager, только чтобы заметить, что фокус никогда не покидает JTable. Возможно, вы и с этим столкнулись. Время для плана Б.

Я получил это "первое нажатие клавиши", добавив KeyListener в таблицу, которая записывает последний KeyEvent для метода keyPressed() в поле экземпляра. Метод getTableCellEditorComponent () считывает символ оттуда. Мне также нужно, чтобы hacky requestFocusInWindow () вызывал вас, если пользователь должен продолжать вводить какие-либо символы после первого.

для моего примера, я создал подкласс 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;
}

что касается гадости, это сидит где-то там с Вогонской поэзией, но это должно решить вашу непосредственную проблему.


я исправил что-то подобное в 2 шага

сначала переопределите editCellAt из JTable и вызовите 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;
}

затем перегрузите requestFocus из JPanel и убедитесь, что ваше текстовое поле помещено как 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();

Я думаю, что я ее решила.
По правде говоря, я не знаю, что решило проблему, так как я использую пользовательский редактор, пользовательский рендеринг и прочее...

когда ячейка выделена, и я нажимаю "abc", на экране появляются 3 буквы (в данном случае ячейка).

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

хорошо... Я пытался... =)
(Я не знаю, так ли это, потому что мой JTable использует jtextfield и JComboBox в качестве редакторов).


у меня была очень похожая проблема. В моем случае у меня был комплекс 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) {
    }
}