2

我有一个 JTable 功能的请求,它允许数据输入更像是在 Excel 中。如果用户在单元格中键入,则先前的值将被新值替换,并且可以使用箭头键在行和列之间导航。

我找到了几个解决这个问题的方法并实施了一个。

然后给了我一个额外的要求——如果用户在表格外点击,输入数据的单元格就会更新(编辑停止)。

我怀疑我只是遗漏了一些东西,但是我实现这两个要求的尝试都失败了。

  1. 我有一个自定义单元格编辑器,可以监听 focusLost 并停止单元格编辑。如果我双击表格然后单击表格外部,这将起作用。但是,如果我只是在单元格中输入了一个数字,则此方法不会运行。

  2. tableMe.setSurrendersFocusOnKeystroke(true); 这 a) 导致我失去了第一次击键 b) 将我置于编辑器中,所以我不能使用左右箭头键移出单元格。

  3. 我在桌子上添加了一个 FocusListener。然后,我可以在一个单元格中输入一个数字并导航到另一个单元格,但我永远无法进入编辑器。双击立即停止编辑。我想我可以在“真正”编辑(在编辑器窗口中有光标)和刚刚在单元格中输入数字时发现一些区别。到目前为止我所尝试的(isEditing() 总是出现 true,hasFocus() 总是出现 false)不起作用。

谁能指出我的解决方案?我的尝试如下。我注释掉了错误的开始,但它们在那里,所以你可以看到我尝试了什么。

import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.text.NumberFormat;
import javax.swing.text.JTextComponent;
import javax.swing.border.*;
import javax.swing.BorderFactory;
import javax.swing.border.Border;

class ExcelTableTest extends JFrame
{
    public static final Color activeDark  = new Color(230, 185, 184);
    public static final Color activeLight = new Color(244, 233, 233);

    private MyJTable      tableMe;
    private MyTableModel  modelMe;
    private JTextField    textMe;
    private JScrollPane   scrollTable;

    private String[]   tableHeaders = { "Column 1", "Column 2" };
    private Object[][] tableData    = new Object[ tableHeaders.length ][ 4 ];

    ExcelTableTest()
    {
        setLayout( new BorderLayout() );
        modelMe  = new MyTableModel(tableData, tableHeaders );
        tableMe  = new MyJTable(modelMe);
        tableMe.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        TableColumn column = null;
        for (int i = 0; i < tableHeaders.length; i++)
        {
           column = tableMe.getColumnModel().getColumn(i);
           column.setCellRenderer( new MyCellRenderer()   );
           column.setCellEditor(   new NumberCellEditor() );
        }
        tableMe.setRowSelectionAllowed(false);
//      tableMe.addFocusListener( new MyFocusListener() );
//      tableMe.setSurrendersFocusOnKeystroke(true);

//      modelMe.addTableModelListener(new MyTableModelListener());
        scrollTable = new JScrollPane(tableMe);
        scrollTable.setVerticalScrollBarPolicy(
            ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
        scrollTable.setHorizontalScrollBarPolicy(
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollTable.setPreferredSize(new Dimension(500,150));

        textMe = new JTextField( 6 );

        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setSize(500,210);
        add(BorderLayout.CENTER, scrollTable );
        add(BorderLayout.SOUTH,  textMe );
        setVisible(true);
    }

    class MyJTable extends JTable
    {
       MyJTable( TableModel model)
       {
          super(model);
       }
//
// Trying to get cells selected when editing starts
       @Override
       public Component prepareEditor(TableCellEditor editor,
                                         int row, int column)
       {
          Component editComponent = super.prepareEditor(editor, row, column);
          if (editComponent instanceof JTextComponent)
          {
             System.out.println( "In prepare editor" );
             ((JTextComponent) editComponent).selectAll();
//           System.out.println( "Requesting Focus" );
//           System.out.println( 
//              ((JTextComponent) editComponent).isFocusable());
//           System.out.println( 
//              ((JTextComponent) editComponent).requestFocusInWindow());
          }
          return editComponent;
       }    
//
// This also works, but end result is the same- when using this, the editor
// does not have focus, so I cannot update the cell just by switching focus
// to another part of the GUI.
//     @Override
//     public boolean editCellAt( int row, int col, EventObject e )
//     {
//        boolean result = super.editCellAt( row, col, e );
//
//        Component editComponent = getEditorComponent();
//        if (  editComponent == null || 
//            !(editComponent instanceof JTextComponent) )
//           return result;
//        if (e instanceof KeyEvent)
//           ((JTextComponent) editComponent).selectAll();
//
//        return result;
//     }
//
    }

    class MyFocusListener implements FocusListener
    {
       @Override
       public void focusGained(FocusEvent e)
       {
          System.out.println("Table has focus");
       }

       @Override
       public void focusLost(FocusEvent e)
       {
          System.out.println("Table lost focus");
          MyJTable        table       = (MyJTable) e.getSource();
          int[]           columns     = table.getSelectedColumns();
//
// I will not be allowing multiple column selection
//
// This is still a problem, as the table IS editing, even though the editor
// does not seem to have focus. This is true whether I am in the case where
// I really have the editor going (in which case the lostFocus of the editor
// nicely cleans things up) or the editor is not really going, and then the
// lostFocus of the editor never triggers.
//        if ( columns.length > 0 && ! table.isEditing() )
          if ( columns.length > 0 )
          {
             TableColumn       tableColumn = table.getColumnModel()
                                                  .getColumn(columns[0]);
             DefaultCellEditor cellEditor  = (DefaultCellEditor)
                                              tableColumn.getCellEditor();
// Does not have focus even when I double click to edit the cell
             if (cellEditor != null && ! cellEditor.getComponent().hasFocus() );
             {
                System.out.println( "Editor focus: " + 
                                     cellEditor.getComponent().hasFocus() );
                if (!cellEditor.stopCellEditing())
                {
                   cellEditor.cancelCellEditing();
                }
             }
          }
       }
    }

    class MyTableModel extends DefaultTableModel
    {

       public MyTableModel(Object rowData[][], Object columnNames[]) {
           super(rowData, columnNames);
       }

       @Override
       public Class getColumnClass(int col) {
          return Double.class;
       }

       @Override
       public boolean isCellEditable(int row, int col)
       {
          return true;
       }
    }

    class MyCellRenderer extends DefaultTableCellRenderer
    {
        public Component getTableCellRendererComponent(
          JTable table, Object value, boolean selected, boolean focus,
          int row, int col) {

          Component renderComponent = super.getTableCellRendererComponent(
             table, value, selected, focus, row, col);
//
// This is called constantly 
//        System.out.println( "In renderer" );

          if ((row % 2) == 0) {
             renderComponent.setBackground(activeDark);
          } else {
             renderComponent.setBackground(activeLight);
          }

          NumberFormat nf = NumberFormat.getInstance();
          nf.setMinimumFractionDigits(0);

          if (value != null)
             System.out.println( "Value: " + value + "; " + value.getClass() );
          super.setText((value == null) ? "" : nf.format(value));
          super.setHorizontalAlignment( SwingConstants.RIGHT );

          return renderComponent;
       }
    }

    class NumberCellEditor extends DefaultCellEditor
    {
       private Double minimum = 0.0;
       private Double value   = null;
       private final  JTextField textField;
       private final  NumberFormat nf = NumberFormat.getInstance();

       public NumberCellEditor()
       {
          super(new JTextField());
          textField = (JTextField) getComponent();
          nf.setMinimumFractionDigits(0);
          textField.setHorizontalAlignment(JTextField.RIGHT);
          textField.addFocusListener(new FocusListener()
          {
             @Override
             public void focusGained(FocusEvent e)
             {
                System.out.println( "In editor" );
                if (value != null)
                   textField.setText(nf.format(value.doubleValue()));
                else
                   textField.setText("");
                textField.setCaretPosition(0);
             }

             @Override
             public void focusLost(FocusEvent e)
             {
                System.out.println( "Lost focus editing cell" );
                if (!stopCellEditing()) cancelCellEditing();
             }
           });
        }

        public boolean stopCellEditing()
        {
          String s = (String)super.getCellEditorValue();
          System.out.println( "Stop Cell editing: " + value );
          if ("".equals(s))
          {
             value = null;
             super.stopCellEditing();
          }
          try
          {
              value = nf.parse(s).doubleValue();
          }
          catch (Exception e)
          {
             textField.setBorder(new LineBorder(Color.red));
             return false;
          }
          return super.stopCellEditing();
       }

       public Component getTableCellEditorComponent(JTable table, Object value,
                                                    boolean isSelected,
                                                    int row, int col)
       {
          this.value = (Double) value;
          textField.setBorder(new LineBorder(Color.black));
          return super.getTableCellEditorComponent(
                          table, value, isSelected, row, col);
       }

       public Object getCellEditorValue()
       {
          value = (Double) value;
          return value;
       }
    }

    public static void main( String[] args )
    {
        ExcelTableTest excelTest = new ExcelTableTest();
    }
}

这是下面描述的简单解决方案。它只需要一行-

        tableMe.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

如果我开始在单元格中输入,然后在单元格中单击鼠标,我会进入编辑器并丢失正在输入的内容。如果我点击其他任何地方,正在输入的内容会被保存并且编辑器会停止。

我尝试的是定义一个标志-

   private boolean       reallyInEditor = true;

然后覆盖 editCellAt 而不是准备编辑器

       @Override
       public boolean editCellAt( int row, int col, EventObject e )
       {
          boolean result = super.editCellAt( row, col, e );

          Component editComponent = getEditorComponent();
          if (  editComponent == null || 
              !(editComponent instanceof JTextComponent) )
             return result;
          if (e instanceof KeyEvent)
          {
             ((JTextComponent) editComponent).selectAll();
             reallyInEditor = false;
         }

          return result;
       }
    }

设置我的标志以指示用户通过键入而不是双击进入编辑器

并将 FocusListener 放在桌子上

        tableMe.addFocusListener( new MyFocusListener() );

    class MyFocusListener implements FocusListener
    {
       @Override
       public void focusGained(FocusEvent e)
       {
          System.out.println("Table has focus");
       }

       @Override
       public void focusLost(FocusEvent e)
       {
          System.out.println("Table lost focus");
          MyJTable        table       = (MyJTable) e.getSource();
          int[]           columns     = table.getSelectedColumns();
//
// I will not be allowing multiple column selection
          if ( columns.length > 0 )
          {
             TableColumn       tableColumn = table.getColumnModel()
                                                  .getColumn(columns[0]);
             DefaultCellEditor cellEditor  = (DefaultCellEditor)
                                              tableColumn.getCellEditor();
             if (cellEditor != null && !reallyInEditor)
             {
                System.out.println( "Editor focus: " + 
                                     cellEditor.getComponent().hasFocus() );
                if (!cellEditor.stopCellEditing())
                {
                   cellEditor.cancelCellEditing();
                }
                reallyInEditor = true;
             }
          }
       }
    }

所有这些额外工作的不同之处在于,如果我开始输入一个单元格,然后单击该单元格,该单元格将使用我输入的值进行更新,并且编辑器会停止。

4

1 回答 1

4

使用以下代码行来实现您正在寻找的内容:

tableMe.putClientProperty("terminateEditOnFocusLost",Boolean.TRUE);
于 2013-01-26T21:22:54.957 回答