问题
I am having some trouble with my JScrollPane/JTextArea when using a SwingWorker. Here is what I currently have:
JTextArea txtDirs;
Task task;
//EDT
public void createGUI(){
txtDirs = new JTextArea();
txtDirs.setBounds(10, 56, 414, 125);
txtDirs.setEditable(false);
contentPane.add(new JScrollPane(txtDirs));
task = new Task();
task.execute();
}
class Task extends SwingWorker<Void, Void>{
@Override
public void doInBackground(){
for(file : files){
txtDirs.append(file.getAbsolutePath);
}
}
@Override
public void done(){
closeWindow();
}
}
This isn't the exact code, but it should give you the gist of it. Anyway, the problem is that the JScrollPane doesn't appear at all, though the code in the doInBackground() method does run. I expect it has something to do with it being updated (txtDirs.append(file.getAbsolutePath())
) in the background task. However, I'm not quite sure how to make it work in this case. The frame appears as expected, but it is entirely blank. Should there be a "process" method in the Task
class? And is that where the "txtDirs.append(file.getAbsolutePath())
" should be placed? I'm afraid I'm rather new to the use of SwingWorkers, so any help would be appreciated.
EDIT:
After a little confusion I decided I'd simply post my full code. I know there are things wrong with it, and I probably misunderstood some of what was mentioned in the comments, so I'm hoping posting this will allow someone to point out any obvious mistakes I've made.
Full code:
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import javax.swing.JProgressBar;
import javax.swing.JLabel;
import javax.swing.SwingWorker;
import org.apache.commons.io.FileUtils;
@SuppressWarnings("serial")
public class BasicCopy extends JFrame {
private JPanel contentPane;
private JTextArea txtCopiedDirs;
private JButton btnCancel;
private JProgressBar progressBar;
private JLabel lblCopying;
private String mainDrive;
private String backupDrive;
private String username;
private String backupDir;
long totalSize = 0L; //total size of directories/files
long currentSize = 0L; //current size of files counting up to ONE_PERCENT
long currentPercent = 0L; //current progress in %
long ONE_PERCENT; //totalSize / 100
Task task;
public BasicCopy() {
}
public BasicCopy(String inDrive, String outDrive, String username, long space){
mainDrive = inDrive;
backupDrive = outDrive;
this.username = username;
totalSize = space*1048576;
ONE_PERCENT = totalSize/100;
createGUI();
}
public void createGUI(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Backup Progress");
setBounds(100, 100, 450, 300);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
txtCopiedDirs = new JTextArea(10, 50);
txtCopiedDirs.setEditable(false);
contentPane.add(new JScrollPane(txtCopiedDirs));
btnCancel = new JButton("Cancel");
btnCancel.setBounds(169, 227, 89, 23);
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
closeWindow();
}
});
contentPane.add(btnCancel);
progressBar = new JProgressBar(0, 100);
progressBar.setBounds(10, 192, 414, 24);
progressBar.setValue(0);
contentPane.add(progressBar);
lblCopying = new JLabel("Now backing up your files....");
lblCopying.setBounds(10, 11, 157, 34);
contentPane.add(lblCopying);
setVisible(true);
task = new Task();
task.execute();
}
/**
* Swing Worker class
*/
class Task extends SwingWorker<Void, String>{
@Override
public Void doInBackground(){
setProgress(0);
//Create Backup Directory
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
String timestamp = sdf.format(date);
backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
File backupDirectory = new File(backupDir);
backupDirectory.mkdir();
//Copy Files
//Main directories
String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
//Backup directories
String bkPics = backupDir + "\\Pictures\\";
String bkDocs = backupDir + "\\Documents\\";
String bkVids = backupDir + "\\Documents\\";
String bkMusc = backupDir + "\\Pictures\\";
String[] directories = {pics, docs, vids, musc};
String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};
//Loop through directories and copy files
for (int i = 0; i < directories.length; i++){
File dir = new File(directories[i]);
File dest = new File(bkDirectories[i]);
for(File file: dir.listFiles()){
try{
if(file.isFile()){
FileUtils.copyFileToDirectory(file, dest);
publish(file.getAbsolutePath() + "\n");
} else if (file.isDirectory()){
FileUtils.copyDirectoryToDirectory(file, dest);
publish(file.getAbsolutePath() + "\n");
}
if(getDirSize(file) >= ONE_PERCENT){
currentPercent = getDirSize(file)/ONE_PERCENT;
progressBar.setValue((int)currentPercent);
currentSize = 0;
} else {
currentSize = currentSize + getDirSize(file);
if(currentSize >= ONE_PERCENT){
currentPercent = currentSize/ONE_PERCENT;
currentPercent++;
progressBar.setValue((int)currentPercent);
currentSize = 0;
}
}
} catch (IOException e){
e.printStackTrace();
}
}
}
return null;
}
@Override
public void process(List<String> chunks){
for(String path : chunks){
txtCopiedDirs.append(path);
}
}
@Override
public void done(){
closeWindow();
}
}
public static Long getDirSize(File file) {
long size = 0L;
if (file.isFile() && file != null){
size += file.isDirectory() ? getDirSize(file) : file.length();
} else if (file.isDirectory()){
for (File f : file.listFiles()) {
size += f.isDirectory() ? getDirSize(f) : file.length();
}
}
return size;
}
/* Close current window */
public void closeWindow() {
WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
System.exit(0);
}
}
EDIT #2:
I have made some changes to the createGUI() method to avoid contentPane from having a null layout:
public void createGUI(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Backup Progress");
setBounds(100, 100, 450, 300);
contentPane = new JPanel(new BorderLayout());
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
txtCopiedDirs = new JTextArea(10, 50);
txtCopiedDirs.setEditable(false);
scrollPane = new JScrollPane(txtCopiedDirs);
contentPane.add(scrollPane, BorderLayout.CENTER);
btnCancel = new JButton("Cancel");
btnCancel.setBounds(169, 227, 89, 23);
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
closeWindow();
}
});
contentPane.add(btnCancel);
progressBar = new JProgressBar(0, 100);
progressBar.setBounds(10, 192, 414, 24);
progressBar.setValue(0);
contentPane.add(progressBar);
lblCopying = new JLabel("Now backing up your files....");
lblCopying.setBounds(10, 11, 157, 34);
contentPane.add(lblCopying);
setVisible(true);
task = new Task();
task.execute();
}
回答1:
- Don't use
setBounds
, instead use JTextArea(rows, cols) - Don't modify the any UI components from outside of the EDT, instead use
publish
and overrideprocess
.process
is called within the content of the EDT
Take a look at Concurrency in Swing for more details
Possible causes of your problem include...
- Adding other components to the frame while it's using a
BorderLayout
, without specifying a position constraint (other thanBorderLayout.CENTER
) - Calling visible before the components are added it
Updated
The main problem is, the contentPane
is using a null
layout and the scroll panes default bounds are 0x0
position by 0x0
width and height.
Best choice is to not use WinodwBuilder and learn how to use layout managers
Update with example
This is an example using two layout managers, BorderLayout
as the base layout and GridBagLayout
to provide some additional support for the extra components.
Basic rule of thumb. If it's a UI component, ESPECIALLY if it's on the screen (or you don't know it's on the screen or not), you must only ever update it from the context of the EDT.
This means calling progressBar.setValue
from within the doInBackground
is a violation of the single thread rule of Swing. SwingWorker
provides progress updates via it's PropertyChange
support
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
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.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.border.EmptyBorder;
public class BasicCopy extends JFrame {
private JPanel contentPane;
private JTextArea txtCopiedDirs;
private JButton btnCancel;
private JProgressBar progressBar;
private JLabel lblCopying;
private String mainDrive;
private String backupDrive;
private String username;
private String backupDir;
long totalSize = 0L; //total size of directories/files
long currentSize = 0L; //current size of files counting up to ONE_PERCENT
long currentPercent = 0L; //current progress in %
long ONE_PERCENT; //totalSize / 100
Task task;
public BasicCopy() {
}
public BasicCopy(String inDrive, String outDrive, String username, long space) {
mainDrive = inDrive;
backupDrive = outDrive;
this.username = username;
totalSize = space * 1048576;
ONE_PERCENT = totalSize / 100;
createGUI();
}
public void createGUI() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle("Backup Progress");
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(new BorderLayout());
txtCopiedDirs = new JTextArea(10, 50);
txtCopiedDirs.setEditable(false);
contentPane.add(new JScrollPane(txtCopiedDirs));
JPanel controls = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(4, 4, 4, 4);
lblCopying = new JLabel("Now backing up your files....");
contentPane.add(lblCopying, gbc);
gbc.gridy++;
progressBar = new JProgressBar(0, 100);
progressBar.setValue(0);
contentPane.add(progressBar, gbc);
gbc.gridy++;
btnCancel = new JButton("Cancel");
btnCancel.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
closeWindow();
}
});
controls.add(btnCancel, gbc);
contentPane.add(controls, BorderLayout.SOUTH);
setLocationRelativeTo(null);
pack();
setVisible(true);
task = new Task();
task.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equalsIgnoreCase(evt.getPropertyName())) {
progressBar.setValue((int) evt.getNewValue());
}
}
});
task.execute();
}
/**
* Swing Worker class
*/
class Task extends SwingWorker<Void, String> {
@Override
public Void doInBackground() throws Exception {
setProgress(0);
//Create Backup Directory
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy_HMMSS");
String timestamp = sdf.format(date);
backupDir = backupDrive + ":\\" + "Backup_" + timestamp;
File backupDirectory = new File(backupDir);
backupDirectory.mkdir();
//Copy Files
//Main directories
String pics = mainDrive + ":\\Users\\" + username + "\\Pictures\\";
String docs = mainDrive + ":\\Users\\" + username + "\\Documents\\";
String vids = mainDrive + ":\\Users\\" + username + "\\Videos\\";
String musc = mainDrive + ":\\Users\\" + username + "\\Music\\";
//Backup directories
String bkPics = backupDir + "\\Pictures\\";
String bkDocs = backupDir + "\\Documents\\";
String bkVids = backupDir + "\\Documents\\";
String bkMusc = backupDir + "\\Pictures\\";
String[] directories = {pics, docs, vids, musc};
String[] bkDirectories = {bkPics, bkDocs, bkVids, bkMusc};
//Loop through directories and copy files
for (int i = 0; i < directories.length; i++) {
File dir = new File(directories[i]);
File dest = new File(bkDirectories[i]);
for (File file : dir.listFiles()) {
try{
if (file.isFile()) {
FileUtils.copyFileToDirectory(file, dest);
publish(file.getAbsolutePath() + "\n");
} else if (file.isDirectory()) {
FileUtils.copyDirectoryToDirectory(file, dest);
Thread.sleep(1000);
publish(file.getAbsolutePath() + "\n");
}
if (getDirSize(file) >= ONE_PERCENT) {
currentPercent = getDirSize(file) / ONE_PERCENT;
setProgress((int) currentPercent);
currentSize = 0;
} else {
currentSize = currentSize + getDirSize(file);
if (currentSize >= ONE_PERCENT) {
currentPercent = currentSize / ONE_PERCENT;
currentPercent++;
setProgress((int) currentPercent);
currentSize = 0;
}
}
} catch (IOException e){
e.printStackTrace();
}
}
}
return null;
}
@Override
public void process(List<String> chunks) {
for (String path : chunks) {
txtCopiedDirs.append(path);
}
}
@Override
public void done() {
closeWindow();
}
}
public static Long getDirSize(File file) {
long size = 0L;
if (file.isFile() && file != null) {
size += file.isDirectory() ? getDirSize(file) : file.length();
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
size += f.isDirectory() ? getDirSize(f) : file.length();
}
}
return size;
}
/* Close current window */
public void closeWindow() {
WindowEvent close = new WindowEvent(this, WindowEvent.WINDOW_CLOSING);
Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(close);
System.exit(0);
}
}
回答2:
Try this, i don't know if append
it's thread-safe, try calling in publish where you know that is called in EDT
.
class Task extends SwingWorker<Void, String>{
@Override
public void doInBackground(){
for(file : files){
publish(file.getAbsolutePath); //or you can store them in an array and call it later
}
}
@Override
public void process(List<String> chunks){
for(String filePath : chunks){
txtDirs.append(filePath);
}
}
@Override
public void done(){
closeWindow();
}
}
If this doesn't help consider posting a SSCCE
来源:https://stackoverflow.com/questions/20778551/implementing-jscrollpane-jtextarea-in-swingworker