使用复合JPanel单元格编辑器编辑后,JTable失去焦点

时间:2011-10-31 19:56:12

标签: java swing jtable tablecelleditor

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

这是一个简单的例子。键入每个单元格并在表格中选项卡。请注意,在访问第3列后,该表将失去焦点到面板上的另一个文本字段。

更新:此问题似乎在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,但解决方案似乎不完整,因为自定义编辑器上的文本字段没有焦点,其光标不显示使用户不清楚该字段可用于输入文字。

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

1 个答案:

答案 0 :(得分:1)

很好的挖掘: - )

对于jdk6,您可以考虑使用SwingX and its JXTable修复该问题(刚检查过,忘记了我们已经解决了一些焦点问题:-)。或者,如果这不是一个选项,请查看其代码并复制重写的transferFocus(及相关)方法和改进的EditorRemover。

不要忘记让你的编辑遵守合同:

       Action action = new AbstractAction() {

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

       };
       m_textfield.setAction(action);