为什么 JPanel 上没有任何显示?

Why is nothing is displayed on JPanel?

我正在尝试从数据库中获取数据并将结果发布到 JPanel

这是简化的代码:

public class LedgerView extends JPanel {
    public LedgerView() {
        super();

        this.setLayout(new FlowLayout());

        this.add(new JLabel("ITEMS:"));

        String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        String DB_URL = "jdbc:mysql://localhost/gnufinance";
        String USERNAME = "gnufinance";
        String PASSWORD = "gnuisnotunix";
        Connection conn = null;

        try {
            Class.forName(JDBC_DRIVER);
            conn = DriverManager.getConnection(DB_URL, USERNAME, PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
        }

        String query = "SELECT * FROM transactions";
        try {
            PreparedStatement ps = conn.prepareStatement(query);
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                String desc = rs.getString(3);
                System.out.println(desc);
                this.add(new JLabel(desc));
            }
        } catch (SQLException e) {
             e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        JFrame view = new JFrame();
        view.setVisible(true);
        view.setSize(300, 300);
        view.add(new LedgerView(), BorderLayout.CENTER);
    }

除了空白的灰色框外,没有显示任何内容,但是当我删除 JDBC 代码时,会显示第一个 JLabel(ITEMS)。屏幕上没有任何内容,但 while 循环中的 System.out.println 会在控制台上正确打印所有数据。无一例外。

您必须在 Event Dispatch Thread 或美国东部时间创建面板。

Swing 不是线程安全的;您对 swing 控件所做的任何更改(例如创建新的 JLabels),必须在 EDT( 而不是 您的 main 方法启动的线程上发生上。您使用 EventQueue.invokeLater() 告诉 Java 将工作移到 Swing 绘图线程上。

请注意,大多数时候您不希望您的工作(如访问数据库)发生在 EDT 上,因为这会使您的用户界面无响应。换句话说,您的程序将执行访问数据库的工作,而不是绘制屏幕。

此代码将解决您眼前的问题,但您应该考虑在其他地方访问数据库(也许 暂时,在您的 main 方法中),存储生成 List,然后将 List 传递给您的 LedgerView,这将导致响应速度更快的用户界面。

public static void main(String[] args) {
    final JFrame view = new JFrame();

    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            view.setSize(300, 300);
            view.add(new LedgerView(), BorderLayout.CENTER);
            view.setVisible(true);
        }
    }
}