如何绕过调用 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 影响的行。
好吧,这让我难过了几天。也许标题不够准确,但这是我唯一能想到的描述我的情况。
我对用户的最终目标是,当他们编辑一行时,仅在第 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 影响的行。