如何在没有JFrame冻结并等待动作侦听器完成的情况下从动作侦听器调用方法?

时间:2015-01-24 20:26:07

标签: java swing jframe jtextarea event-dispatch-thread

如何在JFrame运行时设置JTextArea的文本,并从另一个类刷新JFrame以显示更改?

我有一个带有JTextArea的JFrame作为日志,它打印的字符串我会定期更新来自另一个类的新活动。我的JFrame类(EnablePage)如下所示:

package bot;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import javax.swing.JButton;
import javax.swing.JLabel;
import java.awt.Font;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class EnablePage extends JFrame {
    public static String enablePane;
    private static JPanel contentPane;
    public static JTextArea txtrHello = new JTextArea();

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    EnablePage frame = new EnablePage();
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
    public EnablePage() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 594, 474);
        contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);
        contentPane.setLayout(null);
        JScrollPane scrollPane = new JScrollPane();
        scrollPane.setToolTipText("");
        scrollPane.setBounds(6, 89, 582, 357);
        contentPane.add(scrollPane);
        txtrHello.setEditable(false);
        txtrHello.setText(enablePane);
        txtrHello.setWrapStyleWord(true);
        txtrHello.setLineWrap(true);
        scrollPane.setViewportView(txtrHello);

        JButton btnNewButton = new JButton("Enable");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    navigator.navigator();
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });
        btnNewButton.setBounds(59, 29, 117, 29);
        contentPane.add(btnNewButton);
    }
    public static void update(String x) {
        txtrHello.setText(enablePane+"\n"+x);

    }
}

从我的导航器类中,我一直在尝试使用这行代码来更新JtextArea,同时它操纵一个网站。这段代码我没有包含,但在这里替换为" Thread.sleep(100000);"说明问题:

 package bot;


    import java.text.DateFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;

    import javax.swing.JOptionPane;

    public class navigator {

        public static DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy(HH:mm:ss)");
        public static void navigator() throws Exception {
            Date date1 = new Date();
Thread.sleep(100000);


    EnablePage.update("Bot enabled: "+dateFormat.format(date1));
                 }
        }

但是,这并没有使用新文本更新JFrame,因为EnablePage类一直在等待navigator()方法完成。最终发生的事情是Enable按钮保持蓝色,因为actionlistener方法永远不会被破坏,因为nagivator()方法永远不会完成。我还能做什么来从启用按钮调用navigator()但没有在此行冻结EnablePage类?

2 个答案:

答案 0 :(得分:2)

JTextArea#append将允许您向JTextArea附加文本,setTextappend都是绑定方法,这意味着它们会在调用时触发更新所以你不需要再做任何事了。如果它没有更新,那么听起来你有一个参考问题。

您应该考虑提供一个完整的runnable example来证明您的问题。这将减少混淆和更好的响应

您应该避免使用static,尤其是在与UI组件相关联时,因为这真的开始让您在引用的内容和屏幕上的内容方面遇到麻烦。 static不是对象的交叉通信机制,不应该这样使用。

如果可以,您应该定义某种interface来描述在日志框架上执行的操作(即addLog(String)),让您的日志框架实现此接口,然后传递参考它是那些需要它的类。

或者,您可以使用单例模式允许从应用程序中的任何位置访问日志窗口,个人而言,我很想设计某种类型的队列,其他类将日志事件推送到此(您使用您的框架轮询它或使用某种阻塞队列机制来监视队列的更改。这将要求您有一个单独的Thread(或SwingWorker),它在后台监视队列,因此您不会阻止事件调度线程。

避免使用null布局,像素完美布局是现代ui设计中的一种幻觉。影响组件个体大小的因素太多,您无法控制。 Swing旨在与布局管理器一起工作,放弃这些将导致问题和问题的终结,您将花费越来越多的时间来纠正

<强>更新

你的可运行示例或多或少对我有用。您对static的依赖令人担忧,Thread.sleep(100000);将阻止事件调度线程,使您的程序看起来像挂起(因为它有)。以下是您的示例的返工版本,没有null布局,没有static并且使用Swing Timer而不是Thread.sleep。关于这个的好处是,一旦你按下“启用”按钮,计时器将每秒更新文本区域......

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.Timer;

public class EnablePage extends JFrame {

    private JTextArea txtrHello = new JTextArea(10, 20);

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    EnablePage frame = new EnablePage();
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public EnablePage() {
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        setLayout(new BorderLayout());

        JScrollPane scrollPane = new JScrollPane(txtrHello);
        scrollPane.setToolTipText("");
        add(scrollPane);
        txtrHello.setEditable(false);
        txtrHello.setWrapStyleWord(true);
        txtrHello.setLineWrap(true);

        JButton btnNewButton = new JButton("Enable");
        btnNewButton.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    Navigator.navigator(EnablePage.this);
                } catch (Exception e1) {
                    e1.printStackTrace();
                }
            }
        });
        add(btnNewButton, BorderLayout.NORTH);
    }

    public void update(String x) {
        System.out.println("Update " + x + "\n");
        txtrHello.append(x);

    }

    public static class Navigator {

        public static DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy(HH:mm:ss)");

        public static void navigator(EnablePage page) throws Exception {

            Timer timer = new Timer(1000, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    Date date1 = new Date();
                    page.update("Bot enabled: " + dateFormat.format(date1));
                }
            });
            timer.start();
        }
    }
}

答案 1 :(得分:2)

这是一个简单的例子。时钟JTextField从线程更新。

如您所见,没有更新,验证或无效的方法调用。

编辑添加:调用SwingUtilities invokeLater方法非常重要,以确保在Event Dispatch thread (EDT)上创建和更新Swing组件。

我还修改了Clock示例,在处理JFrame之前干净地停止Timer线程。

package com.ggl.testing;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class Clock implements Runnable {

    private JFrame frame;

    private JTextField clockDisplay;

    private Timer timer;

    @Override
    public void run() {
        frame = new JFrame("Clock");
        frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                exitProcedure();
            }
        });

        JPanel panel = new JPanel();

        clockDisplay = new JTextField(12);
        clockDisplay.setEditable(false);
        clockDisplay.setHorizontalAlignment(JTextField.CENTER);

        panel.add(clockDisplay);

        frame.add(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);

        timer = new Timer(this);
        new Thread(timer).start();
    }

    public void exitProcedure() {
        timer.setRunning(false);
        frame.dispose();
        System.exit(0);
    }

    public void setText(String text) {
        clockDisplay.setText(text);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Clock());
    }

    public class Timer implements Runnable {

        private volatile boolean running;

        private Clock clock;

        private SimpleDateFormat timeFormat;

        public Timer(Clock clock) {
            this.clock = clock;
            this.running = true;
            this.timeFormat = new SimpleDateFormat("h:mm:ss a");
        }

        @Override
        public void run() {
            while (running) {
                displayTime();
                sleep();
            }

        }

        public void displayTime() {
            Calendar calendar = Calendar.getInstance();
            Date date = calendar.getTime();
            final String s = timeFormat.format(date);
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    clock.setText(s);
                }
            });
        }

        public void sleep() {
            try {
                Thread.sleep(200L);
            } catch (InterruptedException e) {
            }
        }

        public synchronized void setRunning(boolean running) {
            this.running = running;
        }

    }

}