JTable内部的JComboBox延迟启动单元编辑

时间:2013-11-24 14:14:30

标签: java swing jcombobox tablecelleditor

我在使用自定义模型的已修改JXComboBox / JXTable中使用RXTable作为单元格编辑器。我开始在单元格内部输入时注意到延迟。

修改:另外,如果您在框中更快地键入更多键,则您键入的第一个键将不会首先出现在编辑器中。

表格:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import org.jdesktop.swingx.JXTable;

/**
 * This is a modified version of RXTable following this thread:
 * http://stackoverflow.com/questions/7365397/combining-jxtable-with-rxtable.
 * The JRXTable provides some extensions to the default JTable
 *
 * 1) Select All editing - when a text related cell is placed in editing mode
 * the text is selected. Controlled by invoking a "setSelectAll..." method.
 *
 * 2) reorderColumns - static convenience method for reodering table columns
 */
public class JRXTable extends JXTable {

    private boolean isSelectAllForMouseEvent = false;
    private boolean isSelectAllForActionEvent = false;
    private boolean isSelectAllForKeyEvent = false;

//
// Constructors
//
    /**
     * Constructs a default
     * <code>JRXTable</code> that is initialized with a default data model, a
     * default column model, and a default selection model.
     */
    public JRXTable() {
        this(null, null, null);
    }

    /**
     * Constructs a
     * <code>JRXTable</code> that is initialized with
     * <code>dm</code> as the data model, a default column model, and a default
     * selection model.
     *
     * @param dm the data model for the table
     */
    public JRXTable(TableModel dm) {
        this(dm, null, null);
    }

    /**
     * Constructs a
     * <code>JRXTable</code> that is initialized with
     * <code>dm</code> as the data model,
     * <code>cm</code> as the column model, and a default selection model.
     *
     * @param dm the data model for the table
     * @param cm the column model for the table
     */
    public JRXTable(TableModel dm, TableColumnModel cm) {
        this(dm, cm, null);
    }

    /**
     * Constructs a
     * <code>JRXTable</code> that is initialized with
     * <code>dm</code> as the data model,
     * <code>cm</code> as the column model, and
     * <code>sm</code> as the selection model. If any of the parameters are
     * <code>null</code> this method will initialize the table with the
     * corresponding default model. The
     * <code>autoCreateColumnsFromModel</code> flag is set to false if
     * <code>cm</code> is non-null, otherwise it is set to true and the column
     * model is populated with suitable
     * <code>TableColumns</code> for the columns in
     * <code>dm</code>.
     *
     * @param dm the data model for the table
     * @param cm the column model for the table
     * @param sm the row selection model for the table
     */
    public JRXTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
        super(dm, cm, sm);
    }

    /**
     * Constructs a
     * <code>JRXTable</code> with
     * <code>numRows</code> and
     * <code>numColumns</code> of empty cells using
     * <code>DefaultTableModel</code>. The columns will have names of the form
     * "A", "B", "C", etc.
     *
     * @param numRows the number of rows the table holds
     * @param numColumns the number of columns the table holds
     */
    public JRXTable(int numRows, int numColumns) {
        this(new DefaultTableModel(numRows, numColumns));
    }

    /**
     * Constructs a
     * <code>JRXTable</code> to display the values in the
     * <code>Vector</code> of
     * <code>Vectors</code>,
     * <code>rowData</code>, with column names,
     * <code>columnNames</code>. The
     * <code>Vectors</code> contained in
     * <code>rowData</code> should contain the values for that row. In other
     * words, the value of the cell at row 1, column 5 can be obtained with the
     * following code:
     * <p>
     * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
     * <p>
     *
     * @param rowData the data for the new table
     * @param columnNames names of each column
     */
    public JRXTable(Vector rowData, Vector columnNames) {
        this(new DefaultTableModel(rowData, columnNames));
    }

    /**
     * Constructs a
     * <code>JRXTable</code> to display the values in the two dimensional array,
     * <code>rowData</code>, with column names,
     * <code>columnNames</code>.
     * <code>rowData</code> is an array of rows, so the value of the cell at row
     * 1, column 5 can be obtained with the following code:
     * <p>
     * <pre> rowData[1][5]; </pre>
     * <p>
     * All rows must be of the same length as
     * <code>columnNames</code>.
     * <p>
     *
     * @param rowData the data for the new table
     * @param columnNames names of each column
     */
    public JRXTable(final Object[][] rowData, final Object[] columnNames) {
        super(rowData, columnNames);
    }
//
//  Overridden methods
//
    /*
     *  Override to provide Select All editing functionality
     */

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


        //  my editing
//
//        if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
//            KeyEvent keyEvent = (KeyEvent) e;
//            Character keyChar = keyEvent.getKeyChar();
//            if (keyChar == KeyEvent.VK_ESCAPE) {
//                return result;
//            }
//        }

        // my editing

        if (isSelectAllForMouseEvent
                || isSelectAllForActionEvent
                || isSelectAllForKeyEvent) {
            selectAll(e);
        }

        return result;
    }

    /*
     * Select the text when editing on a text related cell is started
     */
    private void selectAll(EventObject e) {
        final Component editor = getEditorComponent();

        // add suport for the text editor from a ComboBox
        // move to editCellAt method?
        if (getEditorComponent() instanceof JComboBox) {
            final JComboBox combo = (JComboBox) getEditorComponent();
            ComboBoxEditor comboEditor = combo.getEditor();
            final JTextField comboTextField = (JTextField) comboEditor.getEditorComponent();
//            comboEditor.selectAll();
            if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
                KeyEvent keyEvent = (KeyEvent) e;
                final Character keyChar = keyEvent.getKeyChar();

                if (keyChar == KeyEvent.VK_ESCAPE) {
                    System.out.println("escape");
//                    combo.getFocusCycleRootAncestor().requestFocus();
//                    combo.transferFocus();
                } else {
                    comboEditor.selectAll();
                    comboTextField.setText(comboTextField.getText());
                    SwingUtilities.invokeLater(new Runnable() {
                        @Override
                        public void run() {
                            // set null value if the Delete key is pressed
                            if (keyChar == KeyEvent.VK_DELETE) {
                                combo.setSelectedItem(null);
                                comboTextField.setText(null);
                            } else {
                                comboTextField.selectAll();
//                                comboTextField.setText("");
                            }
                        }
                    });
                }
            }
            return;
        }

        if (editor == null
                || !(editor instanceof JTextComponent
                || editor instanceof JFormattedTextField)) {
            return;
        }

        if (e == null) {
            ((JTextComponent) editor).selectAll();
            return;
        }

        //  Typing in the cell was used to activate the editor

        if (e instanceof KeyEvent && isSelectAllForKeyEvent) {
            ((JTextComponent) editor).selectAll();
            return;
        }

        // If the cell we are dealing with is a JFormattedTextField
        //    force to commit, and invoke selectall

        if (editor instanceof JFormattedTextField) {
            invokeSelectAll((JFormattedTextField) editor);
            return;
        }

        //  F2 was used to activate the editor

        if (e instanceof ActionEvent && isSelectAllForActionEvent) {
            ((JTextComponent) editor).selectAll();
            return;
        }

        //  A mouse click was used to activate the editor.
        //  Generally this is a double click and the second mouse click is
        //  passed to the editor which would remove the text selection unless
        //  we use the invokeLater()

        if (e instanceof MouseEvent && isSelectAllForMouseEvent) {
            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    ((JTextComponent) editor).selectAll();
                }
            });
        }
    }

    private void invokeSelectAll(final JFormattedTextField editor) {
        // old trick: force to commit, and invoke selectall
        editor.setText(editor.getText());
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                editor.selectAll();
            }
        });
    }

//
//  Newly added methods
//
    /*
     *  Sets the Select All property for for all event types
     */
    public void setSelectAllForEdit(boolean isSelectAllForEdit) {
        setSelectAllForMouseEvent(isSelectAllForEdit);
        setSelectAllForActionEvent(isSelectAllForEdit);
        setSelectAllForKeyEvent(isSelectAllForEdit);
    }

    /*
     *  Set the Select All property when editing is invoked by the mouse
     */
    public void setSelectAllForMouseEvent(boolean isSelectAllForMouseEvent) {
        this.isSelectAllForMouseEvent = isSelectAllForMouseEvent;
    }

    /*
     *  Set the Select All property when editing is invoked by the "F2" key
     */
    public void setSelectAllForActionEvent(boolean isSelectAllForActionEvent) {
        this.isSelectAllForActionEvent = isSelectAllForActionEvent;
    }

    /*
     *  Set the Select All property when editing is invoked by
     *  typing directly into the cell
     */
    public void setSelectAllForKeyEvent(boolean isSelectAllForKeyEvent) {
        this.isSelectAllForKeyEvent = isSelectAllForKeyEvent;
    }

//
//  Static, convenience methods
//
    /**
     * Convenience method to order the table columns of a table. The columns are
     * ordered based on the column names specified in the array. If the column
     * name is not found then no column is moved. This means you can specify a
     * null value to preserve the current order of a given column.
     *
     * @param table the table containing the columns to be sorted
     * @param columnNames an array containing the column names in the order they
     * should be displayed
     */
    public static void reorderColumns(JTable table, Object... columnNames) {
        TableColumnModel model = table.getColumnModel();

        for (int newIndex = 0; newIndex < columnNames.length; newIndex++) {
            try {
                Object columnName = columnNames[newIndex];
                int index = model.getColumnIndex(columnName);
                model.moveColumn(index, newIndex);
            } catch (IllegalArgumentException e) {
            }
        }
    }
}  // End of Class JRXTable

测试类:

import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.HashMap;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.table.DefaultTableModel;
import org.jdesktop.swingx.JXComboBox;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.jdesktop.swingx.renderer.DefaultListRenderer;
import org.jdesktop.swingx.renderer.DefaultTableRenderer;
import org.jdesktop.swingx.renderer.StringValue;

public class TestSwingXComboCellEditor {

    public static void main(String[] args) {

        TestSwingXComboCellEditor test = new TestSwingXComboCellEditor();
        test.go();
    }

    public void go() {

        //create the frame
        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // create and add a tabbed pane to the frame
        JTabbedPane tabbedPane = new JTabbedPane();
        frame.getContentPane().add(tabbedPane);
        //create a table and add it to a scroll pane in a new tab
        JRXTable table = new JRXTable();
        table.setModel(new DefaultTableModel(new Object[]{"A", "B"}, 5));
        table.setSelectAllForEdit(true);

        JScrollPane scrollPane = new JScrollPane(table);
        tabbedPane.addTab("test", scrollPane);

        // create a simple JComboBox and set is as table cell editor on column A
        UserRepository rep = new UserRepository();
        UserInfo[] comboElements = rep.getAllUsers();
        DefaultComboBoxModel model = new DefaultComboBoxModel(comboElements);
        JXComboBox comboBox = new JXComboBox(model);

        StringValue stringValue = new StringValue() {
            public String getString(Object value) {
                if (value instanceof UserInfo) {
                    UserInfo userInfo = (UserInfo) value;
                    return userInfo.getFirstName();
                } else {
                    return "";
                }
            }
        };

        ComboBoxCellEditor cellEditor = new ComboBoxCellEditor(comboBox);

        comboBox.setRenderer(new DefaultListRenderer(stringValue));

        comboBox.setEditable(true);
        AutoCompleteDecorator.decorate(comboBox, new ObjectToStringConverter() {
            @Override
            public String getPreferredStringForItem(Object item) {
                if (item instanceof UserInfo) {
                    return ((UserInfo) item).getFirstName();
                } else {
                    return null;
                }

            }
        });
        table.getColumn("A").setCellEditor(cellEditor);
        table.getColumn("A").setCellRenderer(new DefaultTableRenderer(stringValue));

        // pack and show frame
        frame.pack();
        frame.setVisible(true);
    }

    public class UserInfo {

        private String firstName;
        private String lastName;

        public UserInfo(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }

        public String getFirstName() {
            return firstName;
        }

        public String getLastName() {
            return lastName;
        }
    }

    public class UserRepository {

        UserInfo[] comboElements;
        HashMap<String, UserInfo> objectsMap;

        public UserRepository() {
            comboElements = new UserInfo[5];
            comboElements[0] = new UserInfo("John", "Doe");
            comboElements[1] = new UserInfo("Betty", "Doe");
            comboElements[2] = new UserInfo("Elenor", "Smith");
            comboElements[3] = new UserInfo("Helen", "Kelly");
            comboElements[4] = new UserInfo("Joe", "Black");
            objectsMap = new HashMap<>();
            for (int i = 0; i < 5; i++) {
                objectsMap.put(comboElements[i].getFirstName(), comboElements[i]);
            }

        }

        public UserInfo getUserInfo(String name) {
            return objectsMap.get(name);
        }

        public UserInfo[] getAllUsers() {
            return comboElements;
        }
    }

}

0 个答案:

没有答案