JTable 中的单元格在水平滚动后改变外观
Cells in JTable change appearance after scrolling horizontally
我的 Java 8 应用程序在 JScrollPane
中使用 JTable
。目前 table 有超过 10 列,数据是使用 DefaultTableModel
的 addRow(someObjectArray)
添加的。一列中的所有单元格当前都具有相同类型的数据,但列 4+ 可以包含具有 "null" 的单元格(0-3 始终包含数据!= null!)。
如果前三列中的数据与上一行中的数据不相同,则当前行中的前三个单元格以黑色粗体字体书写,否则使用普通的普通字体和深灰色字体 - 这只是一种标记新数据开始的方法:
填充 table 并垂直滚动正确设置粗体和普通字体,但向右滚动然后返回前三列,粗体字体被弄乱了:有时为每个单元格设置,有时对于错误的单元格,有时它会更改中间文本:
再次向下和向上滚动会将单元格重置为它们应有的样子。
这是 table 的设置方式:
JTable myTable = new JTable() {
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
if (column<3) {
if(row==0) { //Always mark column 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
Object prevColumnA = getValueAt(row-1, column); //String
Object currColumnA = getValueAt(row, column); //String
Object prevColumnB = getValueAt(row-1, column+1); //int
Object currColumnB = getValueAt(row, column+1); //int
Object prevColumnC = getValueAt(row-1, column+2); //String
Object currColumnC = getValueAt(row, column+2); //String
if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
markRow = true;
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
markRow = false;
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
} else { //Mark column 1-2 (or not)
if(markRow) {
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
}
}
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
//System.out.println("row="+row+", column="+column+", markRow="+markRow);
return c;
}
};
我该如何解决这个问题?
编辑: MRE 复制自 :
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class SomeMRE extends JPanel {
boolean markRow = false;
public SomeMRE() {
JTable myTable = new JTable() {
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
if (column<3) {
if(row==0) { //Always mark column 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
Object prevColumnA = getValueAt(row-1, column); //String
Object currColumnA = getValueAt(row, column); //String
Object prevColumnB = getValueAt(row-1, column+1); //int
Object currColumnB = getValueAt(row, column+1); //int
Object prevColumnC = getValueAt(row-1, column+2); //String
Object currColumnC = getValueAt(row, column+2); //String
if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
markRow = true;
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
markRow = false;
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
} else { //Mark column 1-2 (or not)
if(markRow) {
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
}
}
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
//System.out.println("row="+row+", column="+column+", markRow="+markRow);
return c;
}
};
myTable.setModel(new DefaultTableModel(
new Object[][] {
{"ColumnA text 1", 123, "ColumnC text 1", 1, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 2, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 3, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 4, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 5, null, "bla", null, null, null},
{"ColumnA text 1", 123, "ColumnC text 1", 6, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 7, null, "bla", null, null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 8, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 9, null, "bla", "bla", null, null}
},
new String[] {
"ColumnA", "ColumnB", "ColumnC", "ColumnD", "ColumnE", "ColumnF", "ColumnG", "ColumnH", "ColumnI"
}
));
setLayout( new BorderLayout() );
add(new JScrollPane(myTable));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("SomeMRE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SomeMRE());
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args) throws Exception {
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
首先,不是 MRE。您陈述的问题是:
but scrolling right and then back to the first three columns, the bold font is messed up:
好吧,您发布的代码不进行水平滚动,因此您无法测试您发布的代码以确保它演示了您陈述的问题。
在测试粗体条件之前,您是否看过我关于删除 "markRow" 变量并设置默认呈现的原始评论?
当我尝试上述建议时,我能够显着简化代码:
JTable myTable = new JTable()
{
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component c = super.prepareRenderer(renderer, row, column);
c.setForeground(Color.DARK_GRAY);
if (column < 3)
{
if(row == 0) { //Always mark columns 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
}
else { // if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
TableModel model = getModel();
Object prevColumn0 = model.getValueAt(row-1, 0); //String
Object currColumn0 = model.getValueAt(row, 0); //String
Object prevColumn1 = model.getValueAt(row-1, 1); //int
Object currColumn1 = model.getValueAt(row, 1); //int
Object prevColumn2 = model.getValueAt(row-1, 2); //String
Object currColumn2 = model.getValueAt(row, 2); //String
if (!prevColumn0.equals(currColumn0)
|| !prevColumn1.equals(currColumn1)
|| !prevColumn2.equals(currColumn2))
{
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
}
}
}
return c;
}
};
From my understand it iterates over the cells that are currently displayed and then sets the renderer for each.
不要假设渲染顺序。每个单元格独立于其他单元格呈现。这就是为什么您不想依赖从上一个正在渲染的单元格设置的变量。
编辑:
更好的 getColumnClass(...)
实现看起来像:
@Override
public Class getColumnClass(int column)
{
switch (column)
{
case 2: return Date.class;
default: return String.class;
}
}
在为 POJO 创建自定义 TableModel 时,您将使用上述方法。有关创建自定义模型的分步方法,请参阅 Row Table Model。
对于从数据库获取数据时可能使用的更通用的实现,您可以使用:
@Override
public Class getColumnClass(int column)
{
for (int row = 0; row < getRowCount(); row++)
{
Object o = getValueAt(row, column);
if (o != null)
{
return o.getClass();
}
}
return Object.class;
}
最好以独立的方式单独确定每个单元格的设置,而不使用在某个特定单元格坐标(例如第一个单元格中的第一个单元格)确定的变量(代码片段中的markRow
)行)。
原因是prepareRenderer
的调用方式没有确定。无法保证首先为第一行中的第一个单元格调用此方法,然后为第一行中的第二个单元格调用,依此类推。换句话说,不要指望 prepareRenderer
会按顺序或任何特定顺序被调用。
另请注意,应为每个单元正确初始化渲染器组件。在 the first version of your question 的代码片段中,起始 if(column<3)
中没有 else
部分,这意味着该组件可能已被任意初始化。如果有准备渲染器的决策树,请确保所有路径都按需要准备渲染器。
我的 Java 8 应用程序在 JScrollPane
中使用 JTable
。目前 table 有超过 10 列,数据是使用 DefaultTableModel
的 addRow(someObjectArray)
添加的。一列中的所有单元格当前都具有相同类型的数据,但列 4+ 可以包含具有 "null" 的单元格(0-3 始终包含数据!= null!)。
如果前三列中的数据与上一行中的数据不相同,则当前行中的前三个单元格以黑色粗体字体书写,否则使用普通的普通字体和深灰色字体 - 这只是一种标记新数据开始的方法:
填充 table 并垂直滚动正确设置粗体和普通字体,但向右滚动然后返回前三列,粗体字体被弄乱了:有时为每个单元格设置,有时对于错误的单元格,有时它会更改中间文本:
再次向下和向上滚动会将单元格重置为它们应有的样子。
这是 table 的设置方式:
JTable myTable = new JTable() {
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
if (column<3) {
if(row==0) { //Always mark column 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
Object prevColumnA = getValueAt(row-1, column); //String
Object currColumnA = getValueAt(row, column); //String
Object prevColumnB = getValueAt(row-1, column+1); //int
Object currColumnB = getValueAt(row, column+1); //int
Object prevColumnC = getValueAt(row-1, column+2); //String
Object currColumnC = getValueAt(row, column+2); //String
if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
markRow = true;
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
markRow = false;
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
} else { //Mark column 1-2 (or not)
if(markRow) {
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
}
}
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
//System.out.println("row="+row+", column="+column+", markRow="+markRow);
return c;
}
};
我该如何解决这个问题?
编辑: MRE 复制自
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
public class SomeMRE extends JPanel {
boolean markRow = false;
public SomeMRE() {
JTable myTable = new JTable() {
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component c = super.prepareRenderer(renderer, row, column);
if (column<3) {
if(row==0) { //Always mark column 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
Object prevColumnA = getValueAt(row-1, column); //String
Object currColumnA = getValueAt(row, column); //String
Object prevColumnB = getValueAt(row-1, column+1); //int
Object currColumnB = getValueAt(row, column+1); //int
Object prevColumnC = getValueAt(row-1, column+2); //String
Object currColumnC = getValueAt(row, column+2); //String
if(!prevColumnA.equals(currColumnA) || !prevColumnB.equals(currColumnB) || !prevColumnC.equals(currColumnC)) {
markRow = true;
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
markRow = false;
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
} else { //Mark column 1-2 (or not)
if(markRow) {
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
}
}
} else {
c.setFont(c.getFont().deriveFont(Font.PLAIN));
c.setForeground(Color.DARK_GRAY);
}
//System.out.println("row="+row+", column="+column+", markRow="+markRow);
return c;
}
};
myTable.setModel(new DefaultTableModel(
new Object[][] {
{"ColumnA text 1", 123, "ColumnC text 1", 1, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 2, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 3, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 4, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 5, null, "bla", null, null, null},
{"ColumnA text 1", 123, "ColumnC text 1", 6, null, "bla", "bla", "bla", null},
{"ColumnA text 2", 234, "ColumnC text 2", 7, null, "bla", null, null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 8, null, "bla", "bla", null, null},
{"ColumnA text 2", 234, "ColumnC text 2", 9, null, "bla", "bla", null, null}
},
new String[] {
"ColumnA", "ColumnB", "ColumnC", "ColumnD", "ColumnE", "ColumnF", "ColumnG", "ColumnH", "ColumnI"
}
));
setLayout( new BorderLayout() );
add(new JScrollPane(myTable));
}
private static void createAndShowGUI() {
JFrame frame = new JFrame("SomeMRE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SomeMRE());
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args) throws Exception {
java.awt.EventQueue.invokeLater( () -> createAndShowGUI() );
}
}
首先,不是 MRE。您陈述的问题是:
but scrolling right and then back to the first three columns, the bold font is messed up:
好吧,您发布的代码不进行水平滚动,因此您无法测试您发布的代码以确保它演示了您陈述的问题。
在测试粗体条件之前,您是否看过我关于删除 "markRow" 变量并设置默认呈现的原始评论?
当我尝试上述建议时,我能够显着简化代码:
JTable myTable = new JTable()
{
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component c = super.prepareRenderer(renderer, row, column);
c.setForeground(Color.DARK_GRAY);
if (column < 3)
{
if(row == 0) { //Always mark columns 0-2 in row 0
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
}
else { // if(column==0) { //Check if column 0-2 should be marked and mark column 0 (or not)
TableModel model = getModel();
Object prevColumn0 = model.getValueAt(row-1, 0); //String
Object currColumn0 = model.getValueAt(row, 0); //String
Object prevColumn1 = model.getValueAt(row-1, 1); //int
Object currColumn1 = model.getValueAt(row, 1); //int
Object prevColumn2 = model.getValueAt(row-1, 2); //String
Object currColumn2 = model.getValueAt(row, 2); //String
if (!prevColumn0.equals(currColumn0)
|| !prevColumn1.equals(currColumn1)
|| !prevColumn2.equals(currColumn2))
{
c.setFont(c.getFont().deriveFont(Font.BOLD));
c.setForeground(Color.BLACK);
}
}
}
return c;
}
};
From my understand it iterates over the cells that are currently displayed and then sets the renderer for each.
不要假设渲染顺序。每个单元格独立于其他单元格呈现。这就是为什么您不想依赖从上一个正在渲染的单元格设置的变量。
编辑:
更好的 getColumnClass(...)
实现看起来像:
@Override
public Class getColumnClass(int column)
{
switch (column)
{
case 2: return Date.class;
default: return String.class;
}
}
在为 POJO 创建自定义 TableModel 时,您将使用上述方法。有关创建自定义模型的分步方法,请参阅 Row Table Model。
对于从数据库获取数据时可能使用的更通用的实现,您可以使用:
@Override
public Class getColumnClass(int column)
{
for (int row = 0; row < getRowCount(); row++)
{
Object o = getValueAt(row, column);
if (o != null)
{
return o.getClass();
}
}
return Object.class;
}
最好以独立的方式单独确定每个单元格的设置,而不使用在某个特定单元格坐标(例如第一个单元格中的第一个单元格)确定的变量(代码片段中的markRow
)行)。
原因是prepareRenderer
的调用方式没有确定。无法保证首先为第一行中的第一个单元格调用此方法,然后为第一行中的第二个单元格调用,依此类推。换句话说,不要指望 prepareRenderer
会按顺序或任何特定顺序被调用。
另请注意,应为每个单元正确初始化渲染器组件。在 the first version of your question 的代码片段中,起始 if(column<3)
中没有 else
部分,这意味着该组件可能已被任意初始化。如果有准备渲染器的决策树,请确保所有路径都按需要准备渲染器。