前情讨论:
最近在做一个Java GUI小程序,点击按钮(JButton)进行一些处理,然后在文本框(JTextArea)中输出相关信息。
看过别人的讨论说,如果处理部分比较复杂且耗时,就会阻塞swing线程,导致swing线程中JTextArea组件对象的内容不能实时刷新:当处理部分的代码运行结束时,JTextArea的内容会一下子刷新。
也就是提示信息不是一条一条显示的,而是在界面卡一段时间之后,一下全部显示。建议另外开启线程来进行复杂的处理。
参考博客:
Java Swing 实时刷新JTextArea,以显示不断append的内容? 这里提到了三种方法,可以按实际情况选择
其中第三种方法的原博是:https://www.cnblogs.com/Forrest-Janny/p/6610759.html (原博更详细一点,建议看原博)
方法一
1.使用JTextArea的paintImmediately方法
JTextArea继承了javax.swing.JComponent 的 paintImmediately 方法
//将info立即追加到JTextArea中
public static void appendJTextArea(String info) {
promptContent.append(info+"\n");
promptContent.paintImmediately(promptContent.getBounds());
}
//将info立即追加到JTextArea中
public static void appendJTextArea(String info) {
promptContent.append(info+"\n");
promptContent.paintImmediately(promptContent.getX(), promptContent.getY(), promptContent.getWidth(), promptContent.getHeight());
}
需要追加信息时,调用编写的appendJTextArea方法即可。效果:可以实时追加。
我的问题:
因为我要追加的内容很多,所以将JTextArea放在了JScrollPane中,超出的部分可通过拖动垂直或水平的滑块观察到。
但仍然出现了问题:JTextArea矩形框大小内的信息是实时更新的,超出部分是在窗口卡住一段时间后,同时添加的。
修正:
还是需要将处理部分另起一个线程来完成,所有的内容都是实时显示的。可以自己编写开启新线程,也可以参考方法三中第1、3段代码。
方法二
使用Swing线程的SwingUtilities.invokeLater 来追加内容
public static void appendJTextArea(String info) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
if(info != null) {
promptContent.append(info+"\n");
}
}
});
}
问题:
情况同方法一相同:需要将处理部分在新开启的线程中完成,可以自己编写,也可以参考方法三第1、3段代码
方法三
https://www.cnblogs.com/Forrest-Janny/p/6610759.html的思路是:JTextArea追加信息时开启一个线程,进行复杂处理时也开启一个线程,这样两个线程之间是不干涉的。
static private ExecutorService service = Executors.newCachedThreadPool(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "output");
}
});
接下来就是,在追加信息方法中开启线程,在复杂处理处开启线程
public static void appendJTextArea(String info) {
service.submit(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
promptContent.append(info+"\n");
}
});
}
//为startButton设置监听器
startButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
service.submit(new Runnable() {
@Override
public void run() {
//这里写自己进行的处理
}
});
}//actionPerformed
});
效果总结:三种方法都可以实时追加信息
补充(我的情况)
处理过程:通过循环实现读取、解析多个文件,每个循环中需要输出多个提示信息,信息之间有一定逻辑联系,例如:打开了xx文件->成功提取了数据->成功存入数据库...
使用方法三时出现了一点问题,它将两个临近的循环中的提示信息交替输出了,我的期望是相同文件的提示一起输出。
交叉输出例子:(同一文件的信息输出应该连着的,这里用相同颜色代表同一文件)
打开了aaa文件
提取了数据
打开了bbb文件
成功存入数据库
提取了数据
成功存入数据库
期望追加信息的效果是:
打开了aaa文件
成功提取了数据
成功存入数据库
打开了bbb文件
成功提取了数据
成功存入数据库
方法一,方法二是按我的预期输出的。