2011-10-31 107 views
3

我有一个单元编辑器,它由JPanel上的多个组件组成。当我的自定义单元格编辑器停止编辑时,表格将失去焦点,而不是将焦点转移到下一个单元格。JTable在使用复合JPanel单元编辑器编辑后失去焦点

下面是一个简单的例子。通过表格键入每个单元格和选项卡。请注意,在访问第三列后,该表将焦点丢到面板上的另一个文本字段。

更新:此问题似乎在Java7中得到解决。该示例必须与Java 6一起运行才能看到焦点丢失的行为。

import java.awt.AWTEvent; 
import java.awt.Color; 
import java.awt.Component; 
import java.awt.EventQueue; 
import java.awt.GridLayout; 
import java.awt.Toolkit; 
import java.awt.event.AWTEventListener; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.event.FocusAdapter; 
import java.awt.event.FocusEvent; 
import javax.swing.AbstractCellEditor; 
import javax.swing.DefaultCellEditor; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 
import javax.swing.JTable; 
import javax.swing.JTextField; 
import javax.swing.table.DefaultTableModel; 
import javax.swing.table.TableCellEditor; 
import javax.swing.table.TableModel; 
import javax.swing.text.JTextComponent; 


public class TableEditorFocusExample extends JFrame 
{ 

    private JTable m_table; 
    private TableModel tableModel; 


    public TableEditorFocusExample() 
    { 
     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     Toolkit.getDefaultToolkit().addAWTEventListener(new AWTEventListener() 
     { 

      @Override 
      public void eventDispatched(AWTEvent event) 
      { 
       System.out.println("FOCUS " + 
            event + 
            "\n source=" + 
            event.getSource()); 
      } 
     }, AWTEvent.FOCUS_EVENT_MASK | AWTEvent.WINDOW_FOCUS_EVENT_MASK); 

     tableModel = new DefaultTableModel(4, 4); 
     m_table = new JTable(tableModel) 
     { 
      @Override 
      public void changeSelection(
        int row, 
        int column, 
        boolean toggle, 
        boolean extend) 
      { 
       super.changeSelection(row, column, toggle, extend); 

       if (editCellAt(row, column)) 
       { 
        Component editor = getEditorComponent(); 
        editor.requestFocusInWindow(); 
        if (editor instanceof JTextComponent) 
        { 
         ((JTextComponent)editor).selectAll(); 
        } 
       } 
      } 

     }; 

     m_table.setModel(tableModel); 
     m_table.setSurrendersFocusOnKeystroke(true); 
     m_table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE); //$NON-NLS-1$  

     DefaultCellEditor textFieldCellEditor = new DefaultCellEditor(new JTextField()); 
     textFieldCellEditor.setClickCountToStart(1); 

     TableCellEditor panelBasedCellEditor = new PanelCellEditor(); 

     m_table.getColumnModel().getColumn(0).setCellEditor(textFieldCellEditor); 
     m_table.getColumnModel().getColumn(1).setCellEditor(textFieldCellEditor); 
     m_table.getColumnModel().getColumn(2).setCellEditor(panelBasedCellEditor); 
     m_table.getColumnModel().getColumn(3).setCellEditor(textFieldCellEditor); 
     m_table.setColumnSelectionAllowed(true); 

     final JButton ok = new JButton("reset"); 

     JPanel panel = new JPanel(); 
     panel.add(m_table); 

     // add a component to grab focus when the table editor loses focus 
     final JTextField textField = new JTextField(8); 
     final Color origTextColor = textField.getBackground(); 
     textField.addFocusListener(new FocusAdapter() 
     { 
      @Override 
      public void focusGained(FocusEvent e) 
      { 
       System.err.println("focus gained from: " + e.getSource()); 
       textField.setBackground(Color.red); 
      } 
     }); 

     // reset the text field background color to the pre-focus color 
     ok.addActionListener(new ActionListener() 
     { 
      @Override 
      public void actionPerformed(ActionEvent e) 
      { 
       textField.setBackground(origTextColor); 
      } 
     }); 

     panel.add(textField); 
     panel.add(ok); 

     getContentPane().add(panel); 
    } 


    public class PanelCellEditor extends AbstractCellEditor implements 
      TableCellEditor 
    { 
     public PanelCellEditor() 
     { 
      m_textfield.setBackground(Color.green); 

      m_panel = new JPanel(new GridLayout()) 
      { 
       @Override 
       public boolean requestFocusInWindow() 
       { 
        // when the table transfers focus to the editor, 
        // forward focus onto the text field. 
        return m_textfield.requestFocusInWindow(); 
       } 
      }; 

      m_panel.add(m_textfield); 
     } 


     @Override 
     public Object getCellEditorValue() 
     { 
      return m_textfield.getText(); 
     } 


     @Override 
     public Component getTableCellEditorComponent(
       JTable table, 
       Object value, 
       boolean isSelected, 
       int row, 
       int column) 
     { 
      m_textfield.setText(value == null ? "" : value.toString()); 
      return m_panel; 
     } 


     private JPanel m_panel; 
     private JTextField m_textfield = new JTextField(5); 
    } 


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

      @Override 
      public void run() 
      { 
       TableEditorFocusExample test = new TableEditorFocusExample(); 
       test.setSize(600, 300); 
       test.setVisible(true); 
      } 
     }); 
    } 
} 

我发现了一个类似的问题here,但解决的办法似乎是不完整的,因为在自定义编辑器的文本字段不具有焦点,它的光标不显示使它不清楚用户的场可用于文本输入。

任何人都有更好的解决方案?

+2

可能会或可能不相关:编辑实现_invalid_:当编辑停止它必须实现它的听众的通知/取消 – kleopatra

+0

是不是AbstraceCellEditor在stopCellEditing()和cancellCellEditing()中处理通知? – esigler

+0

是的,但这只是故事的一部分(当外部合作者显式调用stop/cancelEditing时)缺少的是编辑器内部 - 编辑器 - 根据用户手势在编辑器(fi当按下Enter键进入复合编辑器的内部文本字段时) – kleopatra

回答

1

很好的挖掘:-)

对于JDK6你可以考虑使用SwingX and its JXTable它有固定的问题(刚才检查,忘记了我们曾经身边的一些焦点问题工作:-)。或者,如果这不是一个选项,请查看其代码并复制重写的transferFocus(及相关的)方法和改进的EditorRemover。

而且不要忘记,让您编辑遵守其合同:

 Action action = new AbstractAction() { 

     @Override 
     public void actionPerformed(ActionEvent e) { 
      stopCellEditing(); 
     } 

     }; 
     m_textfield.setAction(action); 
+0

SwingX的JXTable确实解决了这个问题。我也能够使用transferFocus方法来避免向SwingX添加依赖项。 – esigler

+0

顺便说一句,谢谢你在SwingX上的出色工作。期待看到SwingX基础架构问题的解决,以便项目能够复活。 – esigler