为什么JTable视图更新会阻止整个GUI?

时间:2014-06-09 07:50:05

标签: java swing jtable thread-sleep renderer

我有一个简单的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);
            }
        });
    }

}

0 个答案:

没有答案