我有一个带有自定义单元格编辑器和自定义单元格渲染的JTable。这是为了允许在单元格内显示表格。
我需要能够右键单击内部表格单元格,然后选择删除。我的问题现在是识别内部表的单击单元格。它有时可以工作,有时却不起作用(它给出-1作为单元格索引,或者给出错误的单元格索引)。
这是我的代码:
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Arrays;
public class ComplexTable extends JTable {
private int[] colorsArray = {0xbbbbbb, 0xff0000, 0xb28959, 0x318c23, 0xc200f2, 0xbf0000, 0x735839};
private Point clickedLocation;
private JPopupMenu popupMenu;
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
clickedLocation = e.getPoint();
}
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
clickedLocation = e.getPoint();
}
};
public ComplexTable( TableModel tableModel) {
this.setModel(tableModel);
this.addMouseListener(mouseAdapter);
popupMenu = new JPopupMenu();
JMenuItem deleteItem = new JMenuItem("Delete");
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int selectedRow = ComplexTable.this.getSelectedRow();
int selectedColumn = ComplexTable.this.getSelectedColumn();
JTable table = ComplexTable.this;
if(selectedRow < 0){
return;
}
Component c = table.getCellEditor(selectedRow, selectedColumn).
getTableCellEditorComponent(table,
table.getValueAt(selectedRow, selectedColumn), true, selectedRow, selectedColumn);
if (c instanceof JScrollPane) {
JViewport viewport = ((JScrollPane) c).getViewport();
JTable innerTable = (JTable) viewport.getView();
int innerTableSelRow = innerTable.rowAtPoint(clickedLocation);
int innerTableSelCol = innerTable.columnAtPoint(clickedLocation);
System.out.println("inner " + innerTableSelRow + "," + innerTableSelCol);
}
System.out.println("outer " + selectedRow + "," + selectedColumn);
}
});
popupMenu.add(deleteItem);
this.setComponentPopupMenu(popupMenu);
for (int i = 0; i < tableModel.getRowCount(); i++) {
this.setRowHeight(i, 55);
}
TableCellRenderer outerTableCellRenderer = new TableCellRenderer() {
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
// If what we're displaying isn't an ArrayList of values we return the normal renderer
if (((ArrayList<String>) value).size() == 1) {
return table.getDefaultRenderer(((ArrayList<String>) value).get(0).getClass())
.getTableCellRendererComponent(table, ((ArrayList<String>) value).get(0), isSelected, hasFocus, row, column);
} else {
final ArrayList<Object> passed = (ArrayList<Object>) value;
JTable innerTable = new JTable(new AbstractTableModel() {
public int getColumnCount() {
return 1;
}
public int getRowCount() {
return passed.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return passed.get(rowIndex);
}
public boolean isCellEditable(int row, int col) {
return true;
}
});
innerTable.setTableHeader(null);
InnerTableCellRenderer innerTableCellRenderer = new InnerTableCellRenderer();
TableColumnModel columnModel = innerTable.getColumnModel();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
columnModel.getColumn(i).setCellRenderer(innerTableCellRenderer);
}
innerTable.addMouseListener(mouseAdapter);
return new JScrollPane(innerTable);
}
}
};
OuterTableCellEditor outerTableCellEditor = new OuterTableCellEditor();
TableColumnModel tableColumnModel = this.getColumnModel();
for (int i = 0; i < tableColumnModel.getColumnCount(); i++) {
tableColumnModel.getColumn(i).setCellRenderer(outerTableCellRenderer);
tableColumnModel.getColumn(i).setCellEditor(outerTableCellEditor);
}
}
class OuterTableCellEditor extends AbstractCellEditor implements TableCellEditor {
Object value = null;
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
if (((ArrayList<String>) value).size() == 1) {
JTextField textField = new JTextField(((ArrayList<String>) value).get(0));
textField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ArrayList<String> newValue = new ArrayList<>();
newValue.add(((JTextField) e.getSource()).getText());
OuterTableCellEditor.this.value = newValue;
fireEditingStopped();
}
});
textField.setComponentPopupMenu(popupMenu);
return textField;
} else {
final ArrayList<Object> passed = (ArrayList<Object>) value;
final JTable innerTable = new JTable();
innerTable.setTableHeader(null);
AbstractTableModel innerTableModel = new AbstractTableModel() {
public int getColumnCount() {
return 1;
}
public int getRowCount() {
return passed.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return passed.get(rowIndex);
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object value, int row, int col) {
passed.set(row, value);
fireTableCellUpdated(row, col);
}
};
innerTable.setModel(innerTableModel);
InnerTableCellRenderer innerTableCellRenderer = new InnerTableCellRenderer();
TableColumnModel columnModel = innerTable.getColumnModel();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
columnModel.getColumn(i).setCellRenderer(innerTableCellRenderer);
}
innerTable.setComponentPopupMenu(popupMenu);
innerTable.addMouseListener(mouseAdapter);
return new JScrollPane(innerTable);
}
}
@Override
public Object getCellEditorValue() {
return this.value;
}
}
class InnerTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean
isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBackground(new Color(colorsArray[row]));
return c;
}
}
public static void main(String[] args) {
String[] columns = {"Name", "E-Mail", "Phone"};
Object[][] data = new Object[2][columns.length];
ArrayList<Object> namesList = new ArrayList<>();
namesList.add("Peter");
data[0][0] = namesList;
String[] emails = {"[email protected]", "[email protected]"};
data[0][1] = new ArrayList<>(Arrays.asList(emails));
String[] phones = {"555 35 25 65", "555 35 24 63", "555 05 55 55", "555 05 55 66"};
data[0][2] = new ArrayList<Object>(Arrays.asList(phones));
String[] number = {"12.2"};
data[1][0] = new ArrayList<Object>(Arrays.asList(number));
String[] email = {"[email protected]"};
data[1][1] = new ArrayList<Object>(Arrays.asList(email));
String[] phones2 = {"555 35 24 33", "555 11 88 88", "332 55 25 34"};
data[1][2] = new ArrayList<Object>(Arrays.asList(phones2));
AbstractTableModel outerTableModel = new AbstractTableModel() {
public String getColumnName(int col) {
return columns[col];
}
public Class getColumnClass(int col) {
if (getRowCount() < 1) {
return null;
}
return data[0][col].getClass();
}
public int getRowCount() {
return data.length;
}
public int getColumnCount() {
return columns.length;
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object value, int row, int col) {
data[row][col] = value;
fireTableCellUpdated(row, col);
}
};
ComplexTable complexTable = new ComplexTable(outerTableModel);
JFrame frame = new JFrame();
frame.setBounds(100, 100, 700, 400);
frame.setPreferredSize(new Dimension(600, 400));
frame.add(new JScrollPane(complexTable));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
最佳答案
通过观察,外部表依赖于JTable.getSelectedRow()
和JTable.getSelectedColumn()
来获取所选单元格,而内部表依赖于由MouseAdapter
提供的最后单击的位置。
第一个问题是,右键单击不会更改表格单元格的选择。这意味着,如果您选择(左键单击)[0,0]单元格(左上角)并右键单击右下角,则外部选择仍为[0,0]。
第二个问题是MouseAdapter
附加到外部表和内部表。您需要确保事件序列按照您想要的方式发生。由于单击内部表也是外部表的另一个click事件。如果内部表click事件在外部表之前触发,则clickedLocation
将被外部表坐标覆盖,因此其值将为w.r.t外部表。外桌尺寸约为700 * 400,内桌尺寸可能为200 * 80。外部和内部表的x和y具有不同的含义,因为它们未对齐且大小不同。无法通过从外部表捕获的坐标来获取选定的内部单元格。
我根据您的来源修改项目。主要变化是:
对于数据模型,我将单个数据元素保留为字符串,而不是具有单个String的ArrayList(请参见ComplexTableModel.prepareData()
)
我没有将MouseAdapter
添加到表中,而是将动作侦听器添加到JPopupMenu中(请参见TablePopupMenu
)
我在popupMenuWillBecomeVisible()
事件中获取表单元格的选择。
这就是结果
ComplexTable.java
public class ComplexTable extends JTable {
private NestedTableCellRenderer nestedTableRenderer;
private NestedTableCellEditor nestedTableEditor;
private Point outerSelection;
private Point innerSelection;
public ComplexTable(TableModel tm) {
super(tm);
setRowHeight(55);
setComponentPopupMenu(new TablePopupMenu());
nestedTableRenderer = new NestedTableCellRenderer(this);
nestedTableEditor = new NestedTableCellEditor(this);
}
@Override
public TableCellRenderer getCellRenderer(int row, int column) {
Object data = this.getModel().getValueAt(row, column);
if (data instanceof List) {
return nestedTableRenderer;
} else {
return super.getCellRenderer(row, column);
}
}
@Override
public TableCellEditor getCellEditor(int row, int column) {
Object data = this.getModel().getValueAt(row, column);
if (data instanceof List) {
return nestedTableEditor;
} else {
return super.getCellEditor(row, column);
}
}
private void printSelection() {
System.out.println(String.format("outer[%s, %s], inner[%s, %s]", outerSelection.x, outerSelection.y,
innerSelection.x, innerSelection.y));
}
public static void main(String[] args) {
ComplexTable complexTable = new ComplexTable(new ComplexTableModel());
JFrame frame = new JFrame();
frame.setBounds(100, 100, 700, 400);
frame.setPreferredSize(new Dimension(600, 400));
frame.add(new JScrollPane(complexTable));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
class TablePopupMenu extends JPopupMenu implements ActionListener, PopupMenuListener {
private JMenuItem deleteMenuItem;
public TablePopupMenu() {
deleteMenuItem = new JMenuItem("Delete");
deleteMenuItem.addActionListener(this);
add(deleteMenuItem);
addPopupMenuListener(this);
}
@Override
public void actionPerformed(ActionEvent e) {
ComplexTable.this.printSelection();
}
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
// update outer selection on context menu shown
Point mousePoint = ComplexTable.this.getMousePosition();
int row = ComplexTable.this.rowAtPoint(mousePoint);
int col = ComplexTable.this.columnAtPoint(mousePoint);
ComplexTable.this.changeSelection(row, col, false, false);
outerSelection = new Point(row, col);
JTable table = (JTable) this.getInvoker();
if (!(table instanceof ComplexTable)) {
// inner table is a generic swing table, thus the event fires from inner table
innerSelection = new Point(table.getSelectedRow(), table.getSelectedColumn());
} else {
// clear the selection as -1, -1
innerSelection = new Point(-1, -1);
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
}
}
ComplexTableModel.java
public class ComplexTableModel extends AbstractTableModel {
private String[] columnNames = { "Name", "E-Mail", "Phone" };
Object[][] rawData = new Object[2][columnNames.length];
public ComplexTableModel() {
prepareData();
}
private void prepareData() {
rawData[0][0] = "Peter";
rawData[0][1] = Arrays.asList("[email protected]", "[email protected]");
rawData[0][2] = Arrays.asList("555 35 25 65", "555 35 24 63", "555 05 55 55", "555 05 55 66");
rawData[1][0] = "12.2";
rawData[1][1] = "[email protected]";
rawData[1][2] = Arrays.asList("555 35 24 33", "555 11 88 88", "332 55 25 34");
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return true;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
@Override
public int getColumnCount() {
return columnNames.length;
}
@Override
public int getRowCount() {
return rawData.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return rawData[rowIndex][columnIndex];
}
}
NestedTableCellRenderer.java
public class NestedTableCellRenderer implements TableCellRenderer {
private JTable masterTable;
public NestedTableCellRenderer(JTable masterTable) {
this.masterTable = masterTable;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
List<?> valueList = (List<?>) value;
JTable innerTable = new JTable() {
@Override
public boolean isCellEditable(int row, int column) {
return true;
}
};
((DefaultTableModel) innerTable.getModel()).addColumn("", new Vector<Object>(valueList));
innerTable.setDefaultRenderer(Object.class, new ColoredInnerTableCellRenderer());
innerTable.setComponentPopupMenu(masterTable.getComponentPopupMenu());
innerTable.setTableHeader(null);
return new JScrollPane(innerTable);
}
}
class ColoredInnerTableCellRenderer extends DefaultTableCellRenderer {
private int[] colorsArray = { 0xbbbbbb, 0xff0000, 0xb28959, 0x318c23, 0xc200f2, 0xbf0000, 0x735839 };
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
c.setBackground(new Color(colorsArray[row % colorsArray.length]));
return c;
}
}
NestedTableCellEditor.java
public class NestedTableCellEditor extends AbstractCellEditor implements TableCellEditor {
private JTable masterTable;
Object value = null;
public NestedTableCellEditor(JTable masterTable) {
this.masterTable = masterTable;
}
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
this.value = value;
final List<Object> dataList = (List<Object>) value;
final JTable innerTable = new JTable();
innerTable.setTableHeader(null);
AbstractTableModel innerTableModel = new AbstractTableModel() {
public int getColumnCount() {
return 1;
}
public int getRowCount() {
return dataList.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return dataList.get(rowIndex);
}
public boolean isCellEditable(int row, int col) {
return true;
}
public void setValueAt(Object value, int row, int col) {
dataList.set(row, value);
fireTableCellUpdated(row, col);
}
};
innerTable.setModel(innerTableModel);
innerTable.setDefaultRenderer(Object.class, new ColoredInnerTableCellRenderer());
innerTable.setComponentPopupMenu(masterTable.getComponentPopupMenu());
return new JScrollPane(innerTable);
}
@Override
public Object getCellEditorValue() {
return this.value;
}
}