2011-10-31 114 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