【问题标题】:How to bypass calling the JTable renderer如何绕过调用 JTable 渲染器
【发布时间】:2019-12-02 02:11:56
【问题描述】:

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

我对用户的最终目标是,当他们编辑一行时,仅在第 4 列或第 5 列中,表格将突出显示(设置背景颜色为黄色)数据与第 4 列编辑行中的值匹配的任何行,并且5 分别为实际编辑行除外。 (这两列都是jcomboboxes)

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

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

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

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

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

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

【问题讨论】:

  • 代码片段,您已在此处发布,没有提示我们您的问题。当您想在这里获得帮助时,请提供minimal reproducible example(带有 main 方法的小类,仅导入标准 java 类/包,并且可以由 StackOverflow 社区的其他成员运行),重现您的错误行为的步骤以及你想得到什么。
  • 顺便说一句:关于表格单元格渲染器的颜色问题,建议在调用超级实现之前将背景设置为null(插入行setBackground(null);作为方法@987654326的第一行@)。如果此提示不能解决您的问题,请按照我上面的要求提供minimal reproducible example
  • 这不应该很难正常工作——让表格单元格渲染器正常工作有时是一件很棘手的事情,但并非不可能。请帮助我们通过提供上面要求的最小可重现示例来帮助您,这样人们就可以将其放入 Eclipse 或 Netbeans 并快速运行,而无需编写其余代码。
  • 好的,我今天晚些时候再做。

标签: java swing jtable jcombobox tablecellrenderer


【解决方案1】:

一个问题可能是这样的:

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

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

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

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

您应该将匹配的行存储在一个私有字段中,与表的外观无关。比如:

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 的选择颜色保持有效。

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

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 影响的行。

【讨论】:

  • 我仍在使用它并试图掌握它。到目前为止,你给了我很大的启发。我使用最新的 jdk。像 12.0.2 之类的,但我的项目不符合 1.8,所以 lambda 函数让我失望。我以前从未使用过这些,但就我的 Java 知识而言,我只有几个月大。我正在尝试使用它并理解它。谢谢你的帮助!
  • 由于某种原因,这是跳过我的第 7 列(从 0 开始)。它通过 0-6 列,但在通过第 6 列后进入下一行和第 0 列。以前,我使用 setcellrenderer 方法在每个 tablecolumnmodel 的 for 循环中添加渲染器的颜色更改实例。知道这是怎么回事吗?
  • event -&gt; { code } 本质上是实现 TableModelListener 的 one and only method 的简写。 “跳过”是什么意思?你是说匹配行的单元格除了第 7 列的单元格之外都是黄色的?
  • 没错!在调试模式下,我观察渲染器,它为列传递 0-6 而不是 0-7
  • 您确定要在每个 TableColumn 上调用 setCellRenderer 方法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 2012-01-02
  • 2019-01-31
  • 1970-01-01
  • 2021-09-13
相关资源
最近更新 更多