如何绕过调用 JTable 渲染器

How to bypass calling the JTable renderer

好吧,这让我难过了几天。也许标题不够准确,但这是我唯一能想到的描述我的情况。

我对用户的最终目标是,当他们编辑一行时,仅在第 4 列或第 5 列中,table 会突出显示(设置背景色为黄色)数据与分别编辑第 4 列和第 5 列的行,实际编辑行除外。 (这两列都是 jcomboboxes)

听起来不错?好吧,我也试图让这些行突出显示,而当用户使用与之前不同的值编辑第 4 列或第 5 列中的另一行时,仍然重复并突出显示匹配的行,而不是 re-rendering 先前突出显示的行。事实证明这对我来说非常困难,因为我不太明白发生了什么。

最终,当我弄清楚这一点时,用户只需选择该行即可从该行中删除颜色(表示他们已检查该数据)。

我需要知道如何在 jtable 中调用渲染器。似乎每次进行更改时都会调用它。有没有办法渲染 table,然后绕过对渲染器的调用,这样它就不必不断地重新绘制单元格?我不知道我是否在问正确的问题。

我正在尝试覆盖 getTableCellRendererComponent 方法和 return 颜色,但是当我编辑不同的单元格时,我丢失了第一次编辑中突出显示的内容。并且突出显示的内容也不是全部正确,它获取大部分匹配数据以及其他不匹配的行。我不完全理解我猜的渲染器

一定有一个概念我没有掌握。或者也许有一种完全不同的方法来做到这一点。我可以朝正确的方向推动!

public class ColorChange extends DefaultTableCellRenderer {
            @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);
                if (column != 7) {
                    this.setHorizontalAlignment(SwingConstants.CENTER);
                }
                else {
                    this.setHorizontalAlignment(SwingConstants.LEFT);
                }
                Color curColor = c.getBackground();
                if (ColorCheck(table, row, column)) {
                    c.setBackground(Color.YELLOW);
                }
                else if (curColor == Color.YELLOW) {
                    c.setBackground(curColor);
                }
                else {
                    c.setBackground(Color.WHITE);
                }
                return c;
            }

        }

public boolean ColorCheck(JTable jt, int row, int col) {
            boolean result = false;
            int er = jt.getEditingRow();
            int ec = jt.getEditingColumn();
            if (er<0 || ec<0) {
                return result;
            }
            String edMainCat = (String) jt.getValueAt(er, 4);
            String edSubCat = (String) jt.getValueAt(er, 5);
            String MainC = (String) jt.getValueAt(row, 4);
            String SubC = (String) jt.getValueAt(row, 5);

            if (edMainCat == null || edSubCat == null || MainC == null || SubC == null || row == er) {
                return result;
            }
            if (edMainCat.equals(MainC) && edSubCat.equals(SubC)) {
                result = true;
            }

            return result;
        }

一个问题大概是这样的:

Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
// ...
Color curColor = c.getBackground();

super.getTableCellRendererComponent会调用DefaultTableCellRenderer的getTableCellRendererComponent方法。 DefaultTableCellRenderer 永远不会将渲染器组件的背景设置为黄色。 (好吧,理论上,如果 Swing 使用读取用户桌面首选项的外观和感觉,并且用户已设置这些首选项以便按钮背景为黄色,则可以。)

但这并不重要,在这种情况下,因为单元格渲染器最好使用状态信息而不是现有外观。

可以出于多种原因绘制组件(并因此调用它们的单元格渲染器),包括用户将鼠标移到组件上这样简单的事情。没有可靠的方法可以知道究竟是什么导致了它。你能做的就是做好随时调用渲染器的准备。

您应该将匹配行存储在私有字段中,与 table 外观无关。类似于:

public class ColorChange extends DefaultTableCellRenderer {
    private static final long serialVersionUID = 1;

    private final Collection<Integer> matchingRows;

    public ColorChange(Collection<Integer> matchingRows) {
        this.matchingRows = matchingRows;
    }

    @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);

        if (column != 7) {
            this.setHorizontalAlignment(SwingConstants.CENTER);
        } else {
            this.setHorizontalAlignment(SwingConstants.LEFT);
        }

        if (!isSelected) {
            if (matchingRows.contains(row)) {
                c.setBackground(Color.YELLOW);
            } else {
                c.setBackground(null);
            }
        }

        return c;
    }
}

请注意,如果选择了一行,您应该让 JTable 的选择颜色保持有效。

要使用上述渲染器,您将通过侦听 table 模型更改来维护匹配行:

private final Collection<Integer> matchingRows = new HashSet<>();

// ...

    table.setDefaultRenderer(Object.class, new ColorChange(matchingRows));

    table.getModel().addTableModelListener(event -> {
        int type = event.getType();
        int column = event.getColumn();
        TableModel model = (TableModel) event.getSource();
        int firstRow = event.getFirstRow();
        int lastRow = event.getLastRow();

        if (firstRow == TableModelEvent.HEADER_ROW) {
            table.repaint();
            return;
        }

        if (type == TableModelEvent.UPDATE) {
            if ((column == 4 || column == 5) && firstRow == lastRow) {
                int editedRow = firstRow;
                Object edMainC = model.getValueAt(editedRow, 4);
                Object edSubC = model.getValueAt(editedRow, 5);

                matchingRows.clear();
                int count = model.getRowCount();
                for (int row = 0; row < count; row++) {
                    if (row != editedRow) {
                        Object mainC = model.getValueAt(row, 4);
                        Object subC = model.getValueAt(row, 5);
                        if (Objects.equals(mainC, edMainC) ||
                            Objects.equals(subC, edSubC)) {

                            matchingRows.add(row);
                        }
                    }
                }
            }
        } else if (type == TableModelEvent.INSERT) {
            int start = Math.min(firstRow, lastRow);
            int count = Math.abs(lastRow - firstRow) + 1;

            List<Integer> newRows = new ArrayList<>(matchingRows);
            newRows.replaceAll(row -> row < start ? row : row + count);

            matchingRows.clear();
            matchingRows.addAll(newRows);
        } else if (type == TableModelEvent.DELETE) {
            int start = Math.min(firstRow, lastRow);
            int end = Math.max(firstRow, lastRow);
            int count = end - start + 1;

            List<Integer> newRows = new ArrayList<>(matchingRows);
            newRows.removeIf(row -> row >= start && row <= end);
            newRows.replaceAll(row -> row <= end ? row : row - count);

            matchingRows.clear();
            matchingRows.addAll(newRows);
        }

        table.repaint();
    });

需要 repaint(),因为在提交编辑后,每一行都需要(可能)重绘,而不仅仅是受 TableModelEvent 影响的行。