如何在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类?
答案 0 :(得分:2)
JTextArea#append
将允许您向JTextArea
附加文本,setText
和append
都是绑定方法,这意味着它们会在调用时触发更新所以你不需要再做任何事了。如果它没有更新,那么听起来你有一个参考问题。
您应该考虑提供一个完整的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;
}
}
}