简体   繁体   中英

How to bypass calling the JTable renderer

Ok so this has stumped me for a few days. Maybe the title isn't accurate enough but it's the only thing i could think of to describe my situation.

My end goal for the user is that when they edit a row, only in column 4 or 5, will the table highlight (set background color yellow) any rows where the data matches the values in the editing row of column 4 and 5 respectively, EXCEPT for the actual editing row. (both of these columns are jcomboboxes)

sounds good? well i'm also trying to keep those rows highlighted, while when the user edits another row in column 4 or 5 with different values from before, still repeat and highlight the matching rows, while not re-rendering the previously highlighted rows. its proving to be very difficult for me because i dont quite understand whats going on.

eventually, when i figure this out, the way the user will remove the color from the row (signifying they have checked that data) by simply selecting the row.

i need to know how the renderer is called in a jtable. it seems to be called every time a change is made. is there a way to render the table, then bypass the call to the renderer so that it doesnt have to constantly re draw the cells? i don't know if i'm asking the right questions.

i'm trying to override the getTableCellRendererComponent method and return the color that way but when i edit a different cell, i lose what i had highlighted from the first edit. and whats highlighted isnt all that correct either, it gets most of the matching data plus other rows that do not match. i don't fully understand the renderer i guess

there has to be a concept i'm not grasping. or maybe theres a completely different way to do it. i could use a push in the right direction!

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

One issue is probably this:

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

super.getTableCellRendererComponent will call the getTableCellRendererComponent method of DefaultTableCellRenderer. DefaultTableCellRenderer will never set the background of the renderer component to yellow. (Well, in theory, it could, if Swing were using a look-and-feel that reads user desktop preferences, and the user had set those preferences so that button backgrounds are yellow.)

But it doesn't matter, in this case, since it's best for a cell renderer to use state information rather than existing appearance.

Components can be painted (and thus their cell renderers called) for many reasons, including something as simple as the user moving the mouse over the component. There is no reliable way to know what exactly will cause it. All you can do is be prepared for the renderer to be called at any time.

You should store the matching rows in a private field, independent of table appearance. Something like:

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

Notice that if a row is selected, you should let the JTable's selection color remain in effect.

To make use of the above renderer, you would maintain the matching rows by listening for table model changes:

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

The repaint() is needed since every row needs to be (potentially) redrawn, not just the row(s) affected by the TableModelEvent, after an edit is committed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM