我们在多文件下载或多事务处理时,经常会出现使用线程以提高效率的情况,而这时在GUI程序中如何表示进度,就成了一个不大不小的问题。
现在比较被大众接受的方式,大体就是如迅雷等下载工具中一样,用表格中加载进度条以进行显示。
而对于Swing来说,并没有现成的组件能够实现这一操作,还有下载的并发,似乎也需要额外进行处理。于是,我在此提供一个基于jdk1.6版本的示例,以供参考。(因为jdk1.6提供了SwingWorker,简化了图形程序中的线程处理,使用其他jdk开发请替换此项即可)
本示例由两个java文件组成
MyTableModel.java
MyPanel.java
运行效果如下:
现在比较被大众接受的方式,大体就是如迅雷等下载工具中一样,用表格中加载进度条以进行显示。
而对于Swing来说,并没有现成的组件能够实现这一操作,还有下载的并发,似乎也需要额外进行处理。于是,我在此提供一个基于jdk1.6版本的示例,以供参考。(因为jdk1.6提供了SwingWorker,简化了图形程序中的线程处理,使用其他jdk开发请替换此项即可)
本示例由两个java文件组成
MyTableModel.java
package
org.loon.test;
import java.awt.Component;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
/**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
* @author chenpeng
*@email:ceponline@yahoo.com.cn
* @version 0.1
*/
public class MyTableModel extends DefaultTableModel {
/**
*
*/
private static final long serialVersionUID = 1L ;
private static final ColumnContext[]columnArray = {
new ColumnContext( " ID " ,Integer. class , false ),
new ColumnContext( " 名称 " ,String. class , false ),
new ColumnContext( " 进度 " ,Integer. class , false )} ;
private final Map < Integer,SwingWorker > swmap = new HashMap < Integer,SwingWorker > ();
private int number = 0 ;
public void addTest(Testt,SwingWorkerworker) {
Object[]obj = { new Integer(number),t.getName(),t.getProgress()} ;
super .addRow(obj);
swmap.put(number,worker);
number ++ ;
}
public synchronized SwingWorkergetSwingWorker( int identifier) {
Integerkey = (Integer)getValueAt(identifier, 0 );
return swmap.get(key);
}
public TestgetTest( int identifier) {
return new Test((String)getValueAt(identifier, 1 ),
(Integer)getValueAt(identifier, 2 ));
}
public boolean isCellEditable( int row, int col) {
return columnArray[col].isEditable;
}
public Class <?> getColumnClass( int modelIndex) {
return columnArray[modelIndex].columnClass;
}
public int getColumnCount() {
return columnArray.length;
}
public StringgetColumnName( int modelIndex) {
return columnArray[modelIndex].columnName;
}
private static class ColumnContext {
public final StringcolumnName;
public final ClasscolumnClass;
public final boolean isEditable;
public ColumnContext(StringcolumnName,ClasscolumnClass,
boolean isEditable) {
this .columnName = columnName;
this .columnClass = columnClass;
this .isEditable = isEditable;
}
}
}
class Test {
private Stringname;
private Integerprogress;
public Test(Stringname,Integerprogress) {
this .name = name;
this .progress = progress;
}
public void setName(Stringstr) {
name = str;
}
public void setProgress(Integerstr) {
progress = str;
}
public StringgetName() {
return name;
}
public IntegergetProgress() {
return progress;
}
}
class ProgressRenderer extends DefaultTableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 1L ;
private final JProgressBarb = new JProgressBar( 0 , 100 );
public ProgressRenderer() {
super ();
setOpaque( true );
b.setBorder(BorderFactory.createEmptyBorder( 1 , 1 , 1 , 1 ));
}
public ComponentgetTableCellRendererComponent(JTabletable,Objectvalue,
boolean isSelected, boolean hasFocus, int row, int column) {
Integeri = (Integer)value;
Stringtext = " 完成 " ;
if (i < 0 ) {
// 删除
text = " 取消完毕 " ;
} else if (i < 100 ) {
b.setValue(i);
return b;
}
super .getTableCellRendererComponent(table,text,isSelected,hasFocus,
row,column);
return this ;
}
}
import java.awt.Component;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
/**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
* @author chenpeng
*@email:ceponline@yahoo.com.cn
* @version 0.1
*/
public class MyTableModel extends DefaultTableModel {
/**
*
*/
private static final long serialVersionUID = 1L ;
private static final ColumnContext[]columnArray = {
new ColumnContext( " ID " ,Integer. class , false ),
new ColumnContext( " 名称 " ,String. class , false ),
new ColumnContext( " 进度 " ,Integer. class , false )} ;
private final Map < Integer,SwingWorker > swmap = new HashMap < Integer,SwingWorker > ();
private int number = 0 ;
public void addTest(Testt,SwingWorkerworker) {
Object[]obj = { new Integer(number),t.getName(),t.getProgress()} ;
super .addRow(obj);
swmap.put(number,worker);
number ++ ;
}
public synchronized SwingWorkergetSwingWorker( int identifier) {
Integerkey = (Integer)getValueAt(identifier, 0 );
return swmap.get(key);
}
public TestgetTest( int identifier) {
return new Test((String)getValueAt(identifier, 1 ),
(Integer)getValueAt(identifier, 2 ));
}
public boolean isCellEditable( int row, int col) {
return columnArray[col].isEditable;
}
public Class <?> getColumnClass( int modelIndex) {
return columnArray[modelIndex].columnClass;
}
public int getColumnCount() {
return columnArray.length;
}
public StringgetColumnName( int modelIndex) {
return columnArray[modelIndex].columnName;
}
private static class ColumnContext {
public final StringcolumnName;
public final ClasscolumnClass;
public final boolean isEditable;
public ColumnContext(StringcolumnName,ClasscolumnClass,
boolean isEditable) {
this .columnName = columnName;
this .columnClass = columnClass;
this .isEditable = isEditable;
}
}
}
class Test {
private Stringname;
private Integerprogress;
public Test(Stringname,Integerprogress) {
this .name = name;
this .progress = progress;
}
public void setName(Stringstr) {
name = str;
}
public void setProgress(Integerstr) {
progress = str;
}
public StringgetName() {
return name;
}
public IntegergetProgress() {
return progress;
}
}
class ProgressRenderer extends DefaultTableCellRenderer {
/**
*
*/
private static final long serialVersionUID = 1L ;
private final JProgressBarb = new JProgressBar( 0 , 100 );
public ProgressRenderer() {
super ();
setOpaque( true );
b.setBorder(BorderFactory.createEmptyBorder( 1 , 1 , 1 , 1 ));
}
public ComponentgetTableCellRendererComponent(JTabletable,Objectvalue,
boolean isSelected, boolean hasFocus, int row, int column) {
Integeri = (Integer)value;
Stringtext = " 完成 " ;
if (i < 0 ) {
// 删除
text = " 取消完毕 " ;
} else if (i < 100 ) {
b.setValue(i);
return b;
}
super .getTableCellRendererComponent(table,text,isSelected,hasFocus,
row,column);
return this ;
}
}
MyPanel.java
package
org.loon.test;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
// importorg.loon.framework.dll.NativeLoader;
/**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
* @author chenpeng
*@email:ceponline@yahoo.com.cn
* @version 0.1
*/
public class MyPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L ;
private static final ColorevenColor = new Color( 250 , 250 , 250 );
private final MyTableModelmodel = new MyTableModel();
private final TableRowSorter < MyTableModel > sorter = new TableRowSorter < MyTableModel > (
model);
private final JTabletable;
public MyPanel() {
super ( new BorderLayout());
table = new JTable(model) {
/**
*
*/
private static final long serialVersionUID = 1L ;
public ComponentprepareRenderer(
TableCellRenderertableCellRenderer, int row, int column) {
Componentcomponent = super .prepareRenderer(tableCellRenderer,row,
column);
// 背景色及字体设置
if (isRowSelected(row)) {
component.setForeground(getSelectionForeground());
component.setBackground(getSelectionBackground());
} else {
component.setForeground(getForeground());
component.setBackground((row % 2 == 0 ) ? evenColor:table
.getBackground());
}
return component;
}
public JPopupMenugetComponentPopupMenu() {
return makePopup();
}
} ;
table.setRowSorter(sorter);
model.addTest( new Test( " 进度条测试 " , 100 ), null );
// 滚动条
JScrollPanescrollPane = new JScrollPane(table);
// 背景色
scrollPane.getViewport().setBackground(Color.black);
// 弹出菜单
table.setComponentPopupMenu( new JPopupMenu());
// 是否始终大到足以填充封闭视口的高度
table.setFillsViewportHeight( true );
// 将单元格间距的高度和宽度设置为指定的Dimension
table.setIntercellSpacing( new Dimension());
// 是否绘制单元格间的水平线
table.setShowHorizontalLines( true );
// 是否绘制单元格间的垂直线
table.setShowVerticalLines( false );
// 停止编辑时重新定义焦点,避免TableCellEditor丢失数据
table.putClientProperty( " terminateEditOnFocusLost " ,Boolean.TRUE);
// 表示JTable中列的所有属性,如宽度、大小可调整性、最小和最大宽度等。
TableColumncolumn = table.getColumnModel().getColumn( 0 );
column.setMaxWidth( 60 );
column.setMinWidth( 60 );
column.setResizable( false );
column = table.getColumnModel().getColumn( 2 );
// 绘制此列各值的TableCellRenderer
column.setCellRenderer( new ProgressRenderer());
// 添加按钮
add( new JButton( new CreateNewAction( " 添加 " , null )),BorderLayout.SOUTH);
add(scrollPane,BorderLayout.CENTER);
setPreferredSize( new Dimension( 320 , 180 ));
}
class CreateNewAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public CreateNewAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
createNewActionPerformed(evt);
}
}
/**
*创建事件
* @param evt
*/
private void createNewActionPerformed(ActionEventevt) {
final int key = model.getRowCount();
// 在jdk1.6后,当一个Swing程序需要执行一个多线程任务时,可以通过javax.swing.SwingWorker实例进行实现。
// SwingWorker的process可以定义约束属性。更改这些属性将触发事件,并从事件调度线程上引起事件处理方法的调用。
// SwingWorker的done方法,在后台任务完成时自动的在事件调度线程上被调用。
SwingWorker < Integer,Integer > worker = new SwingWorker < Integer,Integer > () {
// 随机sleep
private int sleepDummy = new Random().nextInt( 100 ) + 1 ;
// 最大任务数量
private int taskSize = 200 ;
protected IntegerdoInBackground() {
int current = 0 ;
while (current < taskSize && ! isCancelled()) {
current ++ ;
try {
Thread.sleep(sleepDummy);
} catch (InterruptedExceptionie) {
publish( - 1 );
break ;
}
publish( 100 * current / taskSize);
}
return sleepDummy * taskSize;
}
/**
*进行中处理
*/
protected void process(java.util.List < Integer > data) {
for (Integervalue:data) {
// 把数据填入对应的行列
model.setValueAt(value,key, 2 );
}
// 传送变更事件给指定行列
model.fireTableCellUpdated(key, 2 );
}
/**
*完成后处理
*/
protected void done() {
}
} ;
model.addTest( new Test( " 进度条测试 " , 0 ),worker);
worker.execute();
}
class CancelAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public CancelAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
cancelActionPerformed(evt);
}
}
/**
*取消进度
* @param evt
*/
public synchronized void cancelActionPerformed(ActionEventevt) {
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
return ;
for ( int i = 0 ;i < selection.length;i ++ ) {
int midx = table.convertRowIndexToModel(selection[i]);
SwingWorkerworker = model.getSwingWorker(midx);
if (worker != null && ! worker.isDone()) {
worker.cancel( true );
}
worker = null ;
}
table.repaint();
}
/**
*取消下载进程
*
* @author chenpeng
*
*/
class DeleteAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public DeleteAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
deleteActionPerformed(evt);
}
}
private final HashSet < Integer > set = new HashSet < Integer > ();
public synchronized void deleteActionPerformed(ActionEventevt) {
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
return ;
for ( int i = 0 ;i < selection.length;i ++ ) {
int midx = table.convertRowIndexToModel(selection[i]);
set.add(midx);
SwingWorkerworker = model.getSwingWorker(midx);
if (worker != null && ! worker.isDone()) {
worker.cancel( true );
}
worker = null ;
}
// JTable过滤器
final RowFilter < MyTableModel,Integer > filter = new RowFilter < MyTableModel,Integer > () {
public boolean include(
Entry <? extends MyTableModel, ? extends Integer > entry) {
Integermidx = entry.getIdentifier();
return ! set.contains(midx);
}
} ;
sorter.setRowFilter(filter);
table.repaint();
}
private JPopupMenumakePopup() {
JPopupMenupop = new JPopupMenu();
Actionact = new CreateNewAction( " 添加 " , null );
pop.add(act);
act = new CancelAction( " 取消 " , null );
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
act.setEnabled( false );
pop.add(act);
// 分割线
pop.add( new JSeparator());
act = new DeleteAction( " 删除 " , null );
if (selection == null || selection.length <= 0 )
act.setEnabled( false );
pop.add(act);
return pop;
}
public static void main(String[]args) {
EventQueue.invokeLater( new Runnable() {
public void run() {
createGUI();
}
} );
}
public static void createGUI() {
JFrameframe = new JFrame( " 在JTable中加载进度条及进行操作 " );
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add( new MyPanel());
frame.setSize( 400 , 400 );
// 透明度90%
// NativeLoader.getInstance().setTransparence(frame,0.9f);
// 居中
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Random;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
// importorg.loon.framework.dll.NativeLoader;
/**
*<p>
*Title:LoonFramework
*</p>
*<p>
*Description:
*</p>
*<p>
*Copyright:Copyright(c)2007
*</p>
*<p>
*Company:LoonFramework
*</p>
*
* @author chenpeng
*@email:ceponline@yahoo.com.cn
* @version 0.1
*/
public class MyPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L ;
private static final ColorevenColor = new Color( 250 , 250 , 250 );
private final MyTableModelmodel = new MyTableModel();
private final TableRowSorter < MyTableModel > sorter = new TableRowSorter < MyTableModel > (
model);
private final JTabletable;
public MyPanel() {
super ( new BorderLayout());
table = new JTable(model) {
/**
*
*/
private static final long serialVersionUID = 1L ;
public ComponentprepareRenderer(
TableCellRenderertableCellRenderer, int row, int column) {
Componentcomponent = super .prepareRenderer(tableCellRenderer,row,
column);
// 背景色及字体设置
if (isRowSelected(row)) {
component.setForeground(getSelectionForeground());
component.setBackground(getSelectionBackground());
} else {
component.setForeground(getForeground());
component.setBackground((row % 2 == 0 ) ? evenColor:table
.getBackground());
}
return component;
}
public JPopupMenugetComponentPopupMenu() {
return makePopup();
}
} ;
table.setRowSorter(sorter);
model.addTest( new Test( " 进度条测试 " , 100 ), null );
// 滚动条
JScrollPanescrollPane = new JScrollPane(table);
// 背景色
scrollPane.getViewport().setBackground(Color.black);
// 弹出菜单
table.setComponentPopupMenu( new JPopupMenu());
// 是否始终大到足以填充封闭视口的高度
table.setFillsViewportHeight( true );
// 将单元格间距的高度和宽度设置为指定的Dimension
table.setIntercellSpacing( new Dimension());
// 是否绘制单元格间的水平线
table.setShowHorizontalLines( true );
// 是否绘制单元格间的垂直线
table.setShowVerticalLines( false );
// 停止编辑时重新定义焦点,避免TableCellEditor丢失数据
table.putClientProperty( " terminateEditOnFocusLost " ,Boolean.TRUE);
// 表示JTable中列的所有属性,如宽度、大小可调整性、最小和最大宽度等。
TableColumncolumn = table.getColumnModel().getColumn( 0 );
column.setMaxWidth( 60 );
column.setMinWidth( 60 );
column.setResizable( false );
column = table.getColumnModel().getColumn( 2 );
// 绘制此列各值的TableCellRenderer
column.setCellRenderer( new ProgressRenderer());
// 添加按钮
add( new JButton( new CreateNewAction( " 添加 " , null )),BorderLayout.SOUTH);
add(scrollPane,BorderLayout.CENTER);
setPreferredSize( new Dimension( 320 , 180 ));
}
class CreateNewAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public CreateNewAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
createNewActionPerformed(evt);
}
}
/**
*创建事件
* @param evt
*/
private void createNewActionPerformed(ActionEventevt) {
final int key = model.getRowCount();
// 在jdk1.6后,当一个Swing程序需要执行一个多线程任务时,可以通过javax.swing.SwingWorker实例进行实现。
// SwingWorker的process可以定义约束属性。更改这些属性将触发事件,并从事件调度线程上引起事件处理方法的调用。
// SwingWorker的done方法,在后台任务完成时自动的在事件调度线程上被调用。
SwingWorker < Integer,Integer > worker = new SwingWorker < Integer,Integer > () {
// 随机sleep
private int sleepDummy = new Random().nextInt( 100 ) + 1 ;
// 最大任务数量
private int taskSize = 200 ;
protected IntegerdoInBackground() {
int current = 0 ;
while (current < taskSize && ! isCancelled()) {
current ++ ;
try {
Thread.sleep(sleepDummy);
} catch (InterruptedExceptionie) {
publish( - 1 );
break ;
}
publish( 100 * current / taskSize);
}
return sleepDummy * taskSize;
}
/**
*进行中处理
*/
protected void process(java.util.List < Integer > data) {
for (Integervalue:data) {
// 把数据填入对应的行列
model.setValueAt(value,key, 2 );
}
// 传送变更事件给指定行列
model.fireTableCellUpdated(key, 2 );
}
/**
*完成后处理
*/
protected void done() {
}
} ;
model.addTest( new Test( " 进度条测试 " , 0 ),worker);
worker.execute();
}
class CancelAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public CancelAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
cancelActionPerformed(evt);
}
}
/**
*取消进度
* @param evt
*/
public synchronized void cancelActionPerformed(ActionEventevt) {
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
return ;
for ( int i = 0 ;i < selection.length;i ++ ) {
int midx = table.convertRowIndexToModel(selection[i]);
SwingWorkerworker = model.getSwingWorker(midx);
if (worker != null && ! worker.isDone()) {
worker.cancel( true );
}
worker = null ;
}
table.repaint();
}
/**
*取消下载进程
*
* @author chenpeng
*
*/
class DeleteAction extends AbstractAction {
/**
*
*/
private static final long serialVersionUID = 1L ;
public DeleteAction(Stringlabel,Iconicon) {
super (label,icon);
}
public void actionPerformed(ActionEventevt) {
deleteActionPerformed(evt);
}
}
private final HashSet < Integer > set = new HashSet < Integer > ();
public synchronized void deleteActionPerformed(ActionEventevt) {
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
return ;
for ( int i = 0 ;i < selection.length;i ++ ) {
int midx = table.convertRowIndexToModel(selection[i]);
set.add(midx);
SwingWorkerworker = model.getSwingWorker(midx);
if (worker != null && ! worker.isDone()) {
worker.cancel( true );
}
worker = null ;
}
// JTable过滤器
final RowFilter < MyTableModel,Integer > filter = new RowFilter < MyTableModel,Integer > () {
public boolean include(
Entry <? extends MyTableModel, ? extends Integer > entry) {
Integermidx = entry.getIdentifier();
return ! set.contains(midx);
}
} ;
sorter.setRowFilter(filter);
table.repaint();
}
private JPopupMenumakePopup() {
JPopupMenupop = new JPopupMenu();
Actionact = new CreateNewAction( " 添加 " , null );
pop.add(act);
act = new CancelAction( " 取消 " , null );
int []selection = table.getSelectedRows();
if (selection == null || selection.length <= 0 )
act.setEnabled( false );
pop.add(act);
// 分割线
pop.add( new JSeparator());
act = new DeleteAction( " 删除 " , null );
if (selection == null || selection.length <= 0 )
act.setEnabled( false );
pop.add(act);
return pop;
}
public static void main(String[]args) {
EventQueue.invokeLater( new Runnable() {
public void run() {
createGUI();
}
} );
}
public static void createGUI() {
JFrameframe = new JFrame( " 在JTable中加载进度条及进行操作 " );
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add( new MyPanel());
frame.setSize( 400 , 400 );
// 透明度90%
// NativeLoader.getInstance().setTransparence(frame,0.9f);
// 居中
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
}
运行效果如下: