带有 JCombobox 编辑器的 JTable:处理鼠标点击
JTable with JCombobox editor: handle mouse clicks
我有一个带有 JCombobox 编辑器的 JTable 用于特定列。
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class TablePanel extends JPanel {
public TablePanel() {
JTable table = new JTable(new MyTableModel());
setComboboxColumn(table.getColumnModel().getColumn(1));
add(new JScrollPane(table));
}
public void setComboboxColumn(TableColumn cbColumn) {
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItem("Item 1");
comboBox.addItem("Item 2");
comboBox.addItem("Item 3");
cbColumn.setCellEditor(new DefaultCellEditor(comboBox));
}
private static class MyTableModel extends AbstractTableModel {
private String[] columnNames = {"Normal cell", "Combobox cell"};
private Object[][] data = {
{"Cell 1", "Item 2"},
{"Cell 2", "Item 1"},
{"Cell 3", "Item 1"},
{"Cell 4", "Item 3"},
};
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return data.length;
}
@Override
public String getColumnName(int col) {
return columnNames[col];
}
@Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
@Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
@Override
public boolean isCellEditable(int row, int col) {
return true;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
data[rowIndex][columnIndex] = aValue;
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("TablePanel");
frame.getContentPane().add(new TablePanel());
frame.pack();
frame.setVisible(true);
}
});
}
}
现在发生了什么:
- 当我第一次点击该列的单元格时,组合框弹出窗口会立即出现。
- 如果我单击同一列的其他单元格,组合框会出现,但弹出窗口保持关闭状态。
- 如果我单击其他单元格然后再次返回该列的单元格,组合框弹出窗口会立即再次出现。
我想要什么:
- 首先单击该列的单元格:出现组合框,但弹出列表保持关闭状态。
- 再次单击同一个单元格:出现弹出列表。
我知道我可以使用 cellEditor.setClickCountToStart(2)
但在这种情况下,第二次点击必须在第一次点击后的短时间内执行,我想避免这个限制。
来自BasicComboPopup.Handler#mousePressed(...)
:
public void mousePressed(MouseEvent e) {
if (e.getSource() == list) {
return;
}
if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
return;
//...
togglePopup();
}
您或许可以使用 AncestorListener
:
- 第一次点击该列的单元格时,
combobox.isEnabled()==false
,所以不弹出,后来,AncestorListener#ancestorAdded()
调用combobox.setEnabled(true)
。
- 再次单击同一单元格:
combobox.isEnabled()==true
,出现弹出窗口。
- 如果点击其他单元格:
AncestorListener#ancestorRemoved()
调用combobox.setEnabled(false)
。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
public class ComboBoxCellEditorTogglePopupTest {
private JComboBox<String> makeComboBox() {
JComboBox<String> combobox = new JComboBox<>();
combobox.addItem("Item 1");
combobox.addItem("Item 2");
combobox.addItem("Item 3");
return combobox;
}
public JComponent makeUI() {
String[] columnNames = {"Default", "setEnabled", "String"};
Object[][] data = {
{"Item 1", "Item 1", "aaa"}, {"Item 2", "Item 3", "bbb"}
};
JTable table = new JTable(new DefaultTableModel(data, columnNames));
table.setRowHeight(20);
table.getColumnModel().getColumn(0).setCellEditor(
new DefaultCellEditor(makeComboBox()));
final JComboBox<String> combobox = makeComboBox();
combobox.setEnabled(false);
combobox.addAncestorListener(new AncestorListener() {
@Override public void ancestorAdded(AncestorEvent e) {
System.out.println("ancestorAdded");
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
combobox.setEnabled(true);
}
});
}
@Override public void ancestorRemoved(AncestorEvent e) {
System.out.println("ancestorRemoved");
combobox.setEnabled(false);
}
@Override public void ancestorMoved(AncestorEvent e) {}
});
table.getColumnModel().getColumn(1).setCellEditor(
new DefaultCellEditor(combobox));
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ComboBoxCellEditorTogglePopupTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
我有一个带有 JCombobox 编辑器的 JTable 用于特定列。
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
public class TablePanel extends JPanel {
public TablePanel() {
JTable table = new JTable(new MyTableModel());
setComboboxColumn(table.getColumnModel().getColumn(1));
add(new JScrollPane(table));
}
public void setComboboxColumn(TableColumn cbColumn) {
JComboBox<String> comboBox = new JComboBox<>();
comboBox.addItem("Item 1");
comboBox.addItem("Item 2");
comboBox.addItem("Item 3");
cbColumn.setCellEditor(new DefaultCellEditor(comboBox));
}
private static class MyTableModel extends AbstractTableModel {
private String[] columnNames = {"Normal cell", "Combobox cell"};
private Object[][] data = {
{"Cell 1", "Item 2"},
{"Cell 2", "Item 1"},
{"Cell 3", "Item 1"},
{"Cell 4", "Item 3"},
};
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return data.length;
}
@Override
public String getColumnName(int col) {
return columnNames[col];
}
@Override
public Object getValueAt(int row, int col) {
return data[row][col];
}
@Override
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
@Override
public boolean isCellEditable(int row, int col) {
return true;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
data[rowIndex][columnIndex] = aValue;
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("TablePanel");
frame.getContentPane().add(new TablePanel());
frame.pack();
frame.setVisible(true);
}
});
}
}
现在发生了什么:
- 当我第一次点击该列的单元格时,组合框弹出窗口会立即出现。
- 如果我单击同一列的其他单元格,组合框会出现,但弹出窗口保持关闭状态。
- 如果我单击其他单元格然后再次返回该列的单元格,组合框弹出窗口会立即再次出现。
我想要什么:
- 首先单击该列的单元格:出现组合框,但弹出列表保持关闭状态。
- 再次单击同一个单元格:出现弹出列表。
我知道我可以使用 cellEditor.setClickCountToStart(2)
但在这种情况下,第二次点击必须在第一次点击后的短时间内执行,我想避免这个限制。
来自BasicComboPopup.Handler#mousePressed(...)
:
public void mousePressed(MouseEvent e) {
if (e.getSource() == list) {
return;
}
if (!SwingUtilities.isLeftMouseButton(e) || !comboBox.isEnabled())
return;
//...
togglePopup();
}
您或许可以使用 AncestorListener
:
- 第一次点击该列的单元格时,
combobox.isEnabled()==false
,所以不弹出,后来,AncestorListener#ancestorAdded()
调用combobox.setEnabled(true)
。 - 再次单击同一单元格:
combobox.isEnabled()==true
,出现弹出窗口。 - 如果点击其他单元格:
AncestorListener#ancestorRemoved()
调用combobox.setEnabled(false)
。
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;
public class ComboBoxCellEditorTogglePopupTest {
private JComboBox<String> makeComboBox() {
JComboBox<String> combobox = new JComboBox<>();
combobox.addItem("Item 1");
combobox.addItem("Item 2");
combobox.addItem("Item 3");
return combobox;
}
public JComponent makeUI() {
String[] columnNames = {"Default", "setEnabled", "String"};
Object[][] data = {
{"Item 1", "Item 1", "aaa"}, {"Item 2", "Item 3", "bbb"}
};
JTable table = new JTable(new DefaultTableModel(data, columnNames));
table.setRowHeight(20);
table.getColumnModel().getColumn(0).setCellEditor(
new DefaultCellEditor(makeComboBox()));
final JComboBox<String> combobox = makeComboBox();
combobox.setEnabled(false);
combobox.addAncestorListener(new AncestorListener() {
@Override public void ancestorAdded(AncestorEvent e) {
System.out.println("ancestorAdded");
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
combobox.setEnabled(true);
}
});
}
@Override public void ancestorRemoved(AncestorEvent e) {
System.out.println("ancestorRemoved");
combobox.setEnabled(false);
}
@Override public void ancestorMoved(AncestorEvent e) {}
});
table.getColumnModel().getColumn(1).setCellEditor(
new DefaultCellEditor(combobox));
JPanel p = new JPanel(new BorderLayout());
p.add(new JScrollPane(table));
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new ComboBoxCellEditorTogglePopupTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}