我借助上一篇文章开发了一个带有表格的自动完成组合框,但是当添加到表格单元格并按下一个键时,它会显示以下错误。它在桌子外面工作得很好。
错误:
线程“AWT-EventQueue-0”java.awt.IllegalComponentStateException 中的异常:组件必须显示在屏幕上才能确定其在 java.awt.Component.getLocationOnScreen_NoTreeLock(Component.java:2044) at java.awt.Component.getLocationOnScreen 的位置(Component.java:2018) at javax.swing.JPopupMenu.show(JPopupMenu.java:939) at test.DetailedComboBox$TableComboPopup.show(DetailedComboBox.java:203) at javax.swing.plaf.basic.BasicComboBoxUI.setPopupVisible( BasicComboBoxUI.java:878) at javax.swing.JComboBox.setPopupVisible(JComboBox.java:818) at javax.swing.JComboBox.showPopup(JComboBox.java:803) at test.ComboKeyHandler$1.run(DetailedComboBox.java:339)在 java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:251) 在 java.awt.EventQueue.dispatchEventImpl(EventQueue.java:701) 在 java.awt.EventQueue.access$000(EventQueue.java:102) 在 java.awt.EventQueue$3.run(EventQueue.java:662) 在 java.awt.EventQueue$3.run(EventQueue.java:660)在 java.security.AccessController.doPrivileged(Native Method) 在 java.security.ProtectionDomain$1.doIntersectionPrivilege(ProtectionDomain.java:76) 在 java.awt.EventQueue.dispatchEvent(EventQueue.java:671) 在 java.awt.EventDispatchThread。 pumpOneEventForFilters(EventDispatchThread.java:244) at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:163) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java :147) 在 java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:139) 在 java.awt.EventDispatchThread。运行(EventDispatchThread.java:97)
这是我的代码:
//// 详细组合框
package test;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.basic.BasicComboPopup;
import javax.swing.plaf.basic.ComboPopup;
import javax.swing.plaf.metal.MetalComboBoxUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
/**
*
* @author W.A.R.R.Wijesinghe
*/
public class DetailedComboBox extends JComboBox {
public static enum Alignment {
LEFT, RIGHT
}
private List<List<? extends Object>> tableData;
private String[] columnNames;
private int[] columnWidths;
private int displayColumn;
private Alignment popupAlignment = Alignment.LEFT;
/**
* Construct a TableComboBox object
*/
public DetailedComboBox(String[] colNames, int[] colWidths, int displayColumnIndex) {
super();
this.columnNames = colNames;
this.columnWidths = colWidths;
this.displayColumn = displayColumnIndex;
setUI(new TableComboBoxUI());
setEditable(true);
this.setSelectedIndex(-1);
}
/**
* Set the type of alignment for the popup table
*/
public void setPopupAlignment(Alignment alignment) {
popupAlignment = alignment;
}
/**
* Populate the combobox and drop-down table with the supplied data. If the
* supplied List is non-null and non-empty, it is assumed that the data is a
* List of Lists to be used for the drop-down table. The combobox is also
* populated with the column data from the column defined by
* <code>displayColumn</code>.
*/
public void setTableData(List<List<? extends Object>> tableData) {
this.tableData = (tableData == null
? new ArrayList<List<? extends Object>>() : tableData);
// even though the incoming data is for the table, we must also
// populate the combobox's data, so first clear the previous list.
removeAllItems();
// then load the combobox with data from the appropriate column
Iterator<List<? extends Object>> iter = this.tableData.iterator();
while (iter.hasNext()) {
List<? extends Object> rowData = iter.next();
addItem(rowData.get(displayColumn));
}
}
public List<? extends Object> getSelectedRow() {
return tableData.get(getSelectedIndex());
}
public List<? extends Object> getRowAt(int row) {
return tableData.get(row);
}
/**
* The handler for the combobox's components
*/
private class TableComboBoxUI extends MetalComboBoxUI {
/**
* Create a popup component for the ComboBox
*/
@Override
protected ComboPopup createPopup() {
return new TableComboPopup(comboBox, this);
}
/**
* Return the JList component
*/
public JList getList() {
return listBox;
}
}
/**
* The drop-down of the combobox, which is a JTable instead of a JList.
*/
private class TableComboPopup extends BasicComboPopup implements ListSelectionListener, ItemListener {
private final JTable table;
private TableComboBoxUI comboBoxUI;
private PopupTableModel tableModel;
private JScrollPane scroll;
// private JList list = new JList();
// private ListSelectionListener selectionListener;
// private ItemListener itemListener;
/**
* Construct a popup component that's a table
*/
public TableComboPopup(JComboBox combo, TableComboBoxUI ui) {
super(combo);
this.comboBoxUI = ui;
tableModel = new PopupTableModel();
table = new JTable(tableModel);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
TableColumnModel tableColumnModel = table.getColumnModel();
tableColumnModel.setColumnSelectionAllowed(false);
for (int index = 0; index < table.getColumnCount(); index++) {
TableColumn tableColumn = tableColumnModel.getColumn(index);
tableColumn.setPreferredWidth(columnWidths[index]);
}
scroll = new JScrollPane(table);
scroll.setHorizontalScrollBarPolicy(
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
selectionModel.addListSelectionListener(this);
combo.addItemListener(this);
table.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent event) {
Point p = event.getPoint();
int row = table.rowAtPoint(p);
comboBox.setSelectedIndex(row);
hide();
}
});
table.setBackground(UIManager.getColor("ComboBox.listBackground"));
table.setForeground(UIManager.getColor("ComboBox.listForeground"));
}
/**
* This method is overridden from BasicComboPopup
*/
@Override
public void show() {
if (isEnabled()) {
super.removeAll();
int scrollWidth = 200;//table.getPreferredSize().width
// + ((Integer) UIManager.get("ScrollBar.width")).intValue() + 1;
int scrollHeight = 100;//comboBoxUI.getList().
// getPreferredScrollableViewportSize().height;
scroll.setPreferredSize(new Dimension(scrollWidth, scrollHeight));
super.add(scroll);
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.removeListSelectionListener(this);
selectRow();
selectionModel.addListSelectionListener(this);
int scrollX = 0;
int scrollY = 25;//comboBox.getBounds().height;
/*
if (popupAlignment == Alignment.RIGHT) {
scrollX = comboBox.getBounds().width - scrollWidth;
}
* */
show(comboBox, scrollX, scrollY);
}
}
/**
* Implemention of ListSelectionListener
*/
@Override
public void valueChanged(ListSelectionEvent event) {
comboBox.setSelectedIndex(table.getSelectedRow());
}
/**
* Implemention of ItemListener
*/
@Override
public void itemStateChanged(ItemEvent event) {
if (event.getStateChange() != ItemEvent.DESELECTED) {
ListSelectionModel selectionModel = table.getSelectionModel();
selectionModel.removeListSelectionListener(this);
selectRow();
selectionModel.addListSelectionListener(this);
}
}
/**
* Sync the selected row of the table with the selected row of the
* combo.
*/
private void selectRow() {
int index = comboBox.getSelectedIndex();
if (index != -1) {
table.setRowSelectionInterval(index, index);
table.scrollRectToVisible(table.getCellRect(index, 0, true));
}
}
}
/**
* A model for the popup table's data
*/
private class PopupTableModel extends AbstractTableModel {
/**
* Return the # of columns in the drop-down table
*/
@Override
public int getColumnCount() {
return columnNames.length;
}
/**
* Return the # of rows in the drop-down table
*/
@Override
public int getRowCount() {
return tableData == null ? 0 : tableData.size();
}
/**
* Determine the value for a given cell
*/
@Override
public Object getValueAt(int row, int col) {
if (tableData == null || tableData.isEmpty()) {
return "";
}
return tableData.get(row).get(col);
}
/**
* All cells in the drop-down table are uneditable
*/
@Override
public boolean isCellEditable(int row, int col) {
return false;
}
/**
* Pull the column names out of the tableInfo object for the header
*/
@Override
public String getColumnName(int column) {
String columnName = null;
if (column >= 0 && column < columnNames.length) {
columnName = columnNames[column].toString();
}
return (columnName == null) ? super.getColumnName(column) : columnName;
}
}
}
class ComboKeyHandler extends KeyAdapter {
private final DetailedComboBox comboBox;
// private final Vector<String> list = new Vector<String>();
private final List<List<?>> list = new ArrayList<>();
public ComboKeyHandler(DetailedComboBox combo) {
this.comboBox = combo;
for (int i = 0; i < comboBox.getItemCount(); i++) {
List<? extends Object> rowData = combo.getRowAt(i);
// name.setText(rowData.get(1).toString());
// capital.setText(rowData.get(2).toString());
list.add(rowData);
System.out.println("List Create: " + list);
// list.addElement((String) comboBox.getItemAt(i));
}
}
private boolean shouldHide = false;
@Override
public void keyTyped(final KeyEvent e) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
String text = ((JTextField) e.getSource()).getText();
System.out.println(text);
if (text.length() == 0) {
setSuggestionModel(comboBox, list, "");
comboBox.hidePopup();
} else {
List<List<?>> m = getSuggestedModel(list, text);
if (m.isEmpty() || shouldHide) {
comboBox.hidePopup();
} else {
setSuggestionModel(comboBox, m, text);
comboBox.showPopup();
}
}
}
});
}
@Override
public void keyPressed(KeyEvent e) {
JTextField textField = (JTextField) e.getSource();
String text = textField.getText();
shouldHide = false;
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
if (!list.isEmpty()) {
}
break;
case KeyEvent.VK_ENTER:
if (list.isEmpty()) {
//list.add(new ArrayList<>(Arrays.asList(text, "a", "a")));
// setSuggestionModel(comboBox, getSuggestedModel(list, text), text);
}
shouldHide = true;
break;
case KeyEvent.VK_ESCAPE:
shouldHide = true;
break;
default:
break;
}
}
private static void setSuggestionModel(DetailedComboBox comboBox, List<List<?>> newList, String str) {
comboBox.setTableData(newList);
comboBox.setSelectedIndex(-1);
((JTextField) comboBox.getEditor().getEditorComponent()).setText(str);
}
private static List<List<?>> getSuggestedModel(List<List<?>> list, String text) {
List<List<?>> BacktableData = new ArrayList<>();
System.out.println("List before: " + list);
for (int i = 0; i < list.size(); i++) {
List<List<?>> Innerlist = new ArrayList<>();
Innerlist = (List<List<?>>) list.get(i);
String[] toArray = Innerlist.toArray(new String[Innerlist.size()]);
List<String> Stringlist = new ArrayList<>(Arrays.asList(toArray));
System.out.println("String List ::::::: " + Stringlist);
boolean isSearch = false;
for (Iterator<String> it = Stringlist.iterator(); it.hasNext();) {
isSearch = false;
if (it.next().contains(text)) {
//it.remove(); // NOTE: Iterator's remove method, not ArrayList's, is used.
isSearch = true;
if (isSearch) {
break;
}
}
}
if (isSearch) {
BacktableData.add(new ArrayList<>(Innerlist));
}
System.out.println("BackList : " + BacktableData);
}
System.out.println("Filter Text: " + text);
System.out.println("List After: " + list);
return BacktableData;
/*
* for (String s : list) { if (s.startsWith(text)) { m.addElement(s); }
* } return m;
*
*/
}
}
//////TestTbl
package test;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.DefaultCellEditor;
import javax.swing.JTextField;
import javax.swing.table.TableColumn;
public class TestTbl extends javax.swing.JFrame {
JTextField field;
private DetailedComboBox combo;
public TestTbl() {
initComponents();
List<List<?>> tableData = new ArrayList<>();
tableData.add(new ArrayList<>(Arrays.asList("MD", "Maryland", "Annapolis")));
tableData.add(new ArrayList<>(
Arrays.asList("NH", "New Hampshire", "Concord")));
tableData.add(new ArrayList<>(
Arrays.asList("NJ", "New Jersey", "Trenton")));
tableData.add(new ArrayList<>(
Arrays.asList("NM", "New Mexico", "Santa Fe")));
tableData.add(new ArrayList<>(
Arrays.asList("ND", "North Dakota", "Bismark")));
String[] columns = new String[]{"State", "Name", "Capital"};
int[] widths = new int[]{50, 100, 100};
combo = new DetailedComboBox(columns, widths, 0);
System.out.println(tableData);
combo.setTableData(tableData);
combo.setSelectedIndex(0);
combo.setPopupAlignment(DetailedComboBox.Alignment.LEFT);
field = (JTextField) combo.getEditor().getEditorComponent();
field.setText("");
field.addKeyListener(new ComboKeyHandler(combo));
combo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (field.getText().trim().length() != 0) {
showDetails();
}
}
});
name = new JTextField(10);
capital = new JTextField(10);
name.setEditable(true);
capital.setEditable(false);
TableColumn Item = jTable1.getColumnModel().getColumn(0);
Item.setCellEditor(new DefaultCellEditor(combo));
}
private void showDetails() {
List<? extends Object> rowData = combo.getSelectedRow();
name.setText(rowData.get(1).toString());
capital.setText(rowData.get(2).toString());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
name = new javax.swing.JTextField();
capital = new javax.swing.JTextField();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null, null, null},
{null, null, null, null},
{null, null, null, null},
{null, null, null, null}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
jTable1.setRowHeight(25);
jScrollPane1.setViewportView(jTable1);
name.setText("jTextField1");
capital.setText("jTextField2");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap(82, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 375, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(68, 68, 68))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(name, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(34, 34, 34)
.addComponent(capital, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(110, 110, 110))))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(28, 28, 28)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(name, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(capital, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 45, Short.MAX_VALUE)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 275, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(21, 21, 21))
);
pack();
}// </editor-fold>
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/*
* Set the Nimbus look and feel
*/
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/*
* If Nimbus (introduced in Java SE 6) is not available, stay with the
* default look and feel. For details see
* http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(TestTbl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(TestTbl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(TestTbl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(TestTbl.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/*
* Create and display the form
*/
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new TestTbl().setVisible(true);
}
});
}
// Variables declaration - do not modify
private javax.swing.JTextField capital;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable1;
private javax.swing.JTextField name;
// End of variables declaration
}