我有一个带有自定义单元格编辑器和自定义单元格渲染的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()事件中获取表单元格的选择。


这就是结果
java - 获取内部表的单击单元格-LMLPHP

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;
    }
}

10-06 12:44