在开发Java Swing应用程序的过程中,有两个原则是必须要牢记的:
1.耗时的操作(例如从数据库查询大量数据,读取URI资源等)一定不能运行在EDT(事件派发线程)上,否则会导致Swing用户界面失去响应。
2.只能在EDT线程上对Swing Components进行访问。
基于上面两点原因,在一个Java Swing程序中,要想使用户界面响应灵敏,至少应该有两个线程;一个线程用来执行耗时操作,EDT线程用来执行所有与Swing Components的交互,例如更新文本,重绘图形等等。这就要求两个线程之间要相互通讯,给程序的开发带来了不少的难度,Swing Worker的出现从根本上解决了这个问题,使程序员快速开发反应灵敏的的Swing程序成为可能。SwingWoker被设计应用在此种场景下,你有一个耗时操作需要运行在后台,在该操作完成或部分完成时,你要利用操作返回的结果去更新用户界面。
让我们假想有这样一个应用场景,我有一个保存联系人的文件,我需要从中读取并解析出所有联系人的信息,并及时更新在一个JTable中;假设这个文件非常的大,解析出所有联系人的信息需要花费几分钟的时间,如果不能很好的协调这个任务和EDT线程,则很有可能会造成用户在几分钟时间里得不到结果,而Swing界面处于无响应状态。在这种情况下,SwingWorker就是一个绝佳的选择。我们首先看一下SwingWorker的定义:
public abstract class SwingWorker<T,V> extends Object implements RunnableFuture<T>
显然,这时一个抽象的模板类,在应用的时候,我们需要继承SwingWorker并实例化模板参数。那么,这两个模板类型究竟是什么意思呢,T参数代表的是你的耗时任务执行完成时返回的结果类型,V代表的是你的耗时任务部分完成时返回的结果类型。在我们的场景中,假设任务完成时我们需要一个List<BeanContact>(BeanContact是一个保存联系人信息的JavaBean),每当从文件中解析出一个联系人信息时,我们会新建一个BeanContact并需要更新到JTable中。那么我们的T就是List<BeanContact>,而V就是BeanContact,则应该定义如下的类:
public class LoadContactsTask extends SwingWorker<List<BeanContact>, BeanContact> {
@Override
protected List<BeanContact> doInBackground() throws Exception {
//To do the task and return the result
}
}
从上面可以看到,我们还必须覆盖SwingWorker的doInBackground方法,该方法执行我们的耗时操作,并且返回模板实例化时的T类型结果。下面是具体的代码实现:
@Override
protected List<BeanContact> doInBackground() throws Exception {
BufferedReader reader = new BufferedReader(new FileReader("c:/contacts.cff"));
String line = null;
while ((line = reader.readLine()) != null) {
String[] strContacts = line.split(",");
BeanContact contact = new BeanContact();
contact.setName(strContacts[0]);
contact.setSex(strContacts[1]);
contact.setPhone(strContacts[2]);
contact.setEmail(strContacts[3]);
lineCnt++;
publish(contact);/*********/
contacts.add(contact);
Thread.sleep(100);
}
return contacts;
}
该方法很简单,就是从文件中读取一个联系人的记录并且新建一个BeanContact实例添加到结果集中。我们需要注意的是其中的publish方法,该方法用来发布部分执行结果,每读取一个联系人信息,我们就用该方法把新建的BeanContact发布出去。我们需要知道的是,在publish若干个结果后(可能是一个或多个,由SwingWorker类实现)SwingWorker类的process方法会被自动回调,而我们可以在其中去更新用户界面,SwingWorker保证process方法中所有操作都运行在EDT线程中。下面是我们的具体实现:
@Override
protected void process(List<BeanContact> chunks) {
if (progressHandle != null) {
progressHandle.processInProgress(chunks, lineCnt * 100 / 10000);
}
}
我们的实现中,process中会调用IProgressHandle(自定义的一个接口,用来更新用户界面,详见后面代码)的processInProgress方法来更新用户界面,大家会注意到process方法的参数是一个List<BeanContact>,为什么不是一个BeanContact呢,答案就是我们在上面讲过的,有可能publish若干次后才调用process方法。
与此类似,在doInBackground完成后,SwingWorker会自动调用done方法,下面是我们的实现:
@Override
protected void done() {
if (progressHandle != null) {
progressHandle.processComplete(contacts);
}
}
客户端如何来使用用SwingWork呢,很简单,只需要新建一个实例并且调用它的execute方法即可,他会自动调用doInBackground方法来完成操作;以下是完整的代码实现:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingworkertest;
/**
*
* @author Administrator
*/
public class BeanContact {
private String name=null;
private String sex=null;
private String phone=null;
private String email=null;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the sex
*/
public String getSex() {
return sex;
}
/**
* @param sex the sex to set
*/
public void setSex(String sex) {
this.sex = sex;
}
/**
* @return the phone
*/
public String getPhone() {
return phone;
}
/**
* @param phone the phone to set
*/
public void setPhone(String phone) {
this.phone = phone;
}
/**
* @return the email
*/
public String getEmail() {
return email;
}
/**
* @param email the email to set
*/
public void setEmail(String email) {
this.email = email;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/*
* Contacts.java
*
* Created on 2011-6-25, 10:40:13
*/
package swingworkertest;
import javax.swing.JFileChooser;
/**
*
* @author Administrator
*/
public class Contacts extends javax.swing.JFrame {
/** Creates new form Contacts */
public Contacts() {
initComponents();
handle = new DefaultProgressHandle();
handle.setTable(jTable1);
handle.setProgressBar(jProgressBar1);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
jButton1 = new javax.swing.JButton();
jTextField1 = new javax.swing.JTextField();
jScrollPane1 = new javax.swing.JScrollPane();
jTable1 = new javax.swing.JTable();
jButton2 = new javax.swing.JButton();
jProgressBar1 = new javax.swing.JProgressBar();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
jButton1.setText("浏览");
jButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton1ActionPerformed(evt);
}
});
jTextField1.setEditable(false);
jTable1.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
"姓名", "性别", "电话", "电子邮件"
}
) {
Class[] types = new Class [] {
java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
};
public Class getColumnClass(int columnIndex) {
return types [columnIndex];
}
});
jScrollPane1.setViewportView(jTable1);
jButton2.setText("加载联系人");
jButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButton2ActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
.addComponent(jProgressBar1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
.addComponent(jButton2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, 97, Short.MAX_VALUE)
.addGap(18, 18, 18)
.addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 442, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jButton1)
.addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jButton2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {jButton1, jButton2, jTextField1});
pack();
}// </editor-fold>
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
ContactsFileFilter filter = new ContactsFileFilter();
JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(filter);
int returnVal = chooser.showOpenDialog(null);
if(returnVal == JFileChooser.APPROVE_OPTION){
jTextField1.setText(chooser.getSelectedFile().getAbsolutePath());
}
}
private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
LoadContactsTask task = new LoadContactsTask(jTextField1.getText());
task.setProgressHandle(handle);
task.execute();
}
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
Contacts contacts = new Contacts();
contacts.setTitle("Contacts");;
contacts.setVisible(true);
}
});
}
DefaultProgressHandle handle = null;
// Variables declaration - do not modify
private javax.swing.JButton jButton1;
private javax.swing.JButton jButton2;
private javax.swing.JProgressBar jProgressBar1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTable jTable1;
private javax.swing.JTextField jTextField1;
// End of variables declaration
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingworkertest;
import java.io.File;
import javax.swing.filechooser.FileFilter;
/**
*
* @author Administrator
*/
public class ContactsFileFilter extends FileFilter{
public boolean accept(File pathname) {
if(pathname.isDirectory()){
return true;
}else{
return pathname.getName().endsWith(".cff");
}
}
@Override
public String getDescription() {
return "Text Files";
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingworkertest;
import java.util.List;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
/**
*
* @author Administrator
*/
public class DefaultProgressHandle implements IProgressHandle {
private JTable table = null;
private JProgressBar progressBar = null;
public void processInProgress(List<BeanContact> contacts, int progress) {
DefaultTableModel model = (DefaultTableModel) table.getModel();
for (BeanContact contact : contacts) {
String[] strArray = {contact.getName(), contact.getSex(), contact.getPhone(), contact.getEmail()};
model.addRow(strArray);
}
progressBar.setValue(progress);
}
public void processComplete(List<BeanContact> contacts) {
progressBar.setValue(progressBar.getMaximum());
}
/**
* @param table the table to set
*/
public void setTable(JTable table) {
this.table = table;
}
/**
* @param progressBar the progressBar to set
*/
public void setProgressBar(JProgressBar progressBar) {
this.progressBar = progressBar;
}
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingworkertest;
import java.util.List;
/**
*
* @author Administrator
*/
public interface IProgressHandle {
public abstract void processInProgress(List<BeanContact> contacts,int progress);
public abstract void processComplete(List<BeanContact> contacts);
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package swingworkertest;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingWorker;
/**
*
* @author Administrator
*/
public class LoadContactsTask extends SwingWorker<List<BeanContact>, BeanContact> {
private String fileName = null;
private IProgressHandle progressHandle = null;
private List<BeanContact> contacts = null;
private int lineCnt = 0;
public LoadContactsTask(String fileName) {
this.fileName = fileName;
contacts = new ArrayList<BeanContact>();
}
@Override
protected List<BeanContact> doInBackground() throws Exception {
BufferedReader reader = new BufferedReader(new FileReader("c:/contacts.cff"));
String line = null;
while ((line = reader.readLine()) != null) {
String[] strContacts = line.split(",");
BeanContact contact = new BeanContact();
contact.setName(strContacts[0]);
contact.setSex(strContacts[1]);
contact.setPhone(strContacts[2]);
contact.setEmail(strContacts[3]);
lineCnt++;
publish(contact);
contacts.add(contact);
Thread.sleep(100);
}
return contacts;
}
/**
* @param progressHandle the progressHandle to set
*/
public void setProgressHandle(IProgressHandle progressHandle) {
this.progressHandle = progressHandle;
}
@Override
protected void process(List<BeanContact> chunks) {
if (progressHandle != null) {
progressHandle.processInProgress(chunks, lineCnt * 100 / 10000);
}
}
@Override
protected void done() {
if (progressHandle != null) {
progressHandle.processComplete(contacts);
}
}
}
相关推荐
这个实例集可能会涵盖上述所有知识点,甚至包括高级主题如Swing worker线程用于在后台执行任务,避免阻塞UI,以及JTable和JTree等用于显示表格和树状数据的组件。通过深入学习这些实例,开发者可以提升自己在Java ...
5. **Swing入门-第七讲源码**:可能涉及Swing的高级特性,如Swing Worker,用于在后台线程执行耗时任务,避免阻塞UI。此外,也可能讲解组件的状态管理,如JCheckBox、JRadioButton和ButtonGroup。 6. **Swing入门-...
7. **Swing worker线程**:Swing不是线程安全的,因此在进行耗时操作时,如网络请求或数据库操作,应使用SwingWorker来避免阻塞事件调度线程,保持用户界面的响应性。 8. **国际化与本地化**:Swing支持多语言应用...
7. **Swing worker线程** - 为了防止UI线程阻塞,Swing引入了SwingWorker,用于在后台线程执行耗时任务,然后将结果传递回主线程更新界面。 8. **对话框(Dialogs)** - JOptionPane 提供了标准的对话框,如消息...
11. **Swing worker线程**: - Swing不是线程安全的,因此需要使用SwingWorker来执行耗时任务,避免阻塞用户界面。 通过学习以上知识点,并结合提供的源码,你将能够创建一个完整的Java Swing应用,包括具有选项卡...
在这个特定的示例中,`TestSwingWorker`可能是一个使用了Swing Worker类的程序,这是Java Swing库提供的一个工具,用于在后台执行耗时任务并同时更新UI。 Swing Worker是Java Swing中用于在后台线程执行计算并在...
在本文中,我们将深入探讨Swing线程的理解和SwingWorker基础知识,并通过实例代码演示如何正确地使用Swing线程来解决常见的问题。 Swing线程的理解 Swing线程是Java中一个特殊的线程,用于处理图形用户界面的事件...
总之,Java Swing CRUD应用是Java桌面开发中常见的实践,它涵盖了基础的数据库操作和GUI设计,是学习Java GUI编程和数据库连接的重要实例。通过熟练掌握这些知识,开发者能够创建功能完备的桌面数据管理应用。
在Java编程环境中,Swing库提供了...以上是关于"Swing不确定进度条"的知识点介绍,希望对你理解如何在Java Swing应用程序中使用不确定进度条有所帮助。在实际开发中,可以根据具体需求进行调整和优化,提升用户体验。
Java图形用户界面(GUI)是Java编程...这个教程会通过实例讲解这些概念,帮助你从基础开始,逐步掌握使用Swing创建高效、易用的Java GUI应用的技能。通过深入学习和实践,你可以构建出满足各种业务需求的桌面应用程序。
10. **Swing worker线程**:在GUI程序中,为了避免阻塞主线程(事件调度线程),可以使用SwingWorker进行耗时操作,如数据库查询或网络通信。 11. **国际化与本地化**:对于一个全面的管理系统,可能会考虑支持多...
- 创建一个后台线程(`SwingWorker`实例),负责从URL读取数据并写入本地文件。 - 在读取过程中,定期计算已下载的字节数与总字节数的比例,更新进度条的值。 - 当下载完成时,通知用户并关闭相关流。 6. **事件...
`JButton`是`AbstractButton`类的实例,可以通过传递字符串或图标作为按钮的显示文本或图像来创建。例如: ```java JButton button = new JButton("点击我"); ``` ### 2. 添加事件监听器 为了响应按钮的点击事件...
通过这个"Java图像编程实例库",开发者不仅可以学习到基础的GUI编程技巧,还能深入理解Java2D和Swing等技术,为开发出富有视觉吸引力的应用程序打下坚实的基础。对于初学者来说,通过实践这些实例,可以快速上手并...
总的来说,基于Java+Swing实现的管道工人游戏是一个集教育与娱乐于一体的作品,它展示了Java语言在桌面应用开发中的强大能力,同时也提供了一个实践和学习Java编程的实例。无论是对Java初学者还是有经验的开发者,都...
11. **Swing Worker和JavaFX Task**:为了在界面操作中避免阻塞主线程,可以使用Swing Worker(针对Swing)或JavaFX Task(针对JavaFX)。例子可能展示了如何在后台执行耗时任务,并更新界面状态。 12. **数据绑定*...
高级界面篇则关注更高级的界面技巧,如自定义组件的创建、国际化和本地化支持、以及Swing的高级特性,如JTable、JTree和JList的使用,以及Swing worker线程,用于在后台执行耗时任务,保持界面的响应性。 数据库...
8. **Swing Worker**:使用此类来实现后台线程处理,避免阻塞用户界面,提高应用性能。 9. **外观和感觉(LookAndFeel)**:定制或改变应用程序的视觉风格,使其适应不同操作系统或用户偏好。 10. **JTable和JTree**...
SwingWorker是Swing提供的一种机制,用于在后台线程(也称为工作线程或worker threads)中执行耗时任务,而不阻塞EDT。它允许在后台线程执行计算,同时在主线程更新UI,保持界面的响应性。SwingWorker支持两种类型...