我有一个简单的JTable,使用复杂的自定义单元格渲染器显示许多数据(~1000行)。渲染这个细胞需要一点时间。在此期间,整个GUI似乎都被冻结了。这可以看出,因为向用户显示了进度指示器。在数据生成期间,它正常工作。但如果通过fireTableDataChanged
触发JTable来更新其视图,则进度指示器将被冻结。
在下面的小例子中,您应该按下按钮并在生成数据后查看效果。静态等待块(通过Thread.sleep()
)表示用于数据生成和表示生成的复杂现实世界代码。
现在我的问题是我的错。因为我不相信,所以没有可能解决这个问题。
澄清:
在我的应用程序中,每个单元格都以两行显示不同颜色和图标的数据。为了显示一个简单的例子,我只添加了sleep来指示创建和呈现所有这些单元格需要很长时间。请注意,我的应用程序显示了大约1000或2000个这两个具有不同颜色和图标的线条单元格。这需要花费很多时间来渲染。而且由于这个简单的例子无法显示这种复杂性,我添加了睡眠(应该代表复杂的工作)。
你问“为什么需要这么久?”。正如我之前解释过的,有数千个单元格,它们会对每个单元格中的每个对象调用操作并显示设置。它实际上可能不会那么长,但为了显示问题,我将这个时间设置为特定的42毫秒。
我知道当我阻止EDT时,GUI会冻结。但我不知道,我在程序中的任何地方都阻止了EDT。 sleep语句用于表示执行数据生成或设置每个单元格的许多代码行。
“......没有理由创造一些东西,也没有JComponents,因为JLabel是...”。我需要一个多行单元格,能够为每一行着色并为每一行添加一个图标。我发现的最佳方式就是将JPanel上的两个JLabel作为单元格。如果有一种更简单的方法,我会很高兴听到。我已经尝试了一个带有多行HTML的JList。问题是,如果有很多不同的行,它往往会出错。
“......你的目标是什么......” - 这应该是简单的工作。是否可以更改此示例以显示“正在运行”的不确定进度条,直到表完全更新而不删除Thread.sleep()
语句?
package example;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
public class Example extends DefaultTableModel {
static List<String> data = new ArrayList<>();
@Override
public int getRowCount() {
return data.size();
}
@Override
public Object getValueAt(int row, int column) {
return data.get(row);
}
@Override
public String getColumnName(int column) {
return "Column 0";
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
public void updateView() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
fireTableDataChanged();
}
});
}
static class CustomCellRenderer extends JPanel implements TableCellRenderer {
private JLabel line1, line2;
public CustomCellRenderer() {
super();
setOpaque(true);
setLayout(new BorderLayout());
this.line1 = new JLabel();
this.line1.setOpaque(false);
add(this.line1, BorderLayout.CENTER);
this.line2 = new JLabel();
this.line2.setOpaque(false);
add(this.line2, BorderLayout.SOUTH);
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
this.line1.setForeground(table.getSelectionForeground());
this.line2.setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
} else {
setForeground(table.getForeground());
this.line1.setForeground(table.getForeground());
this.line2.setForeground(table.getForeground());
setBackground(table.getBackground());
}
// This wait represents other complex layout preparations
// for this cell
try {
Thread.sleep(42);
} catch (Exception e) {
}
line1.setText("Value: " + value);
line2.setText("isSelected? " + isSelected + ", hasFocus?" + hasFocus);
return this;
}
}
static JPanel overlay;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
JFrame frame = new JFrame("Example");
frame.setLayout(new BorderLayout(4, 4));
// Add JTable
final Example model = new Example();
JTable table = new JTable(model) {
@Override
public void doLayout() {
TableColumn col = getColumnModel().getColumn(0);
for (int row = 0; row < getRowCount(); row++) {
Component c = prepareRenderer(col.getCellRenderer(), row, 0);
setRowHeight(row, c.getPreferredSize().height);
}
super.doLayout();
}
};
TableColumn col = table.getColumnModel().getColumn(0);
col.setCellRenderer(new CustomCellRenderer());
frame.add(new JScrollPane(table), BorderLayout.CENTER);
// Add button
Box hBox = Box.createHorizontalBox();
hBox.add(new JButton(new AbstractAction("Load data") {
@Override
public void actionPerformed(ActionEvent e) {
new Thread(new Runnable() {
@Override
public void run() {
overlay.setVisible(true);
data.clear();
System.out.println("Generating data ...");
for (int i = 0; i < 42; i++) {
data.add("String no. " + (i + 1));
try {
Thread.sleep(42);
} catch (Exception e) {
}
}
System.out.println("Updating view ...");
model.updateView();
overlay.setVisible(false);
System.out.println("Finished.");
}
}).start();
}
}));
hBox.add(Box.createHorizontalGlue());
frame.add(hBox, BorderLayout.NORTH);
// Create loading overlay
overlay = new JPanel(new FlowLayout(FlowLayout.CENTER)) {
@Override
protected void paintComponent(Graphics g) {
g.setColor(new Color(0, 0, 0, 125));
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
overlay.setOpaque(false);
overlay.setBackground(new Color(0, 0, 0, 125));
JProgressBar bar = new JProgressBar();
bar.setIndeterminate(true);
overlay.add(bar);
frame.setGlassPane(overlay);
frame.getGlassPane().setVisible(false);
// Create frame
frame.setSize(600, 400);
frame.setVisible(true);
}
});
}
}