阳光男孩

Never give up!

Entries Tagged ‘Swing’

反射在JavaSwing中的应用

0顶一下反射在JavaSwing中的应用,学习过Java Swing的读者一定对于Swing中相对较为复杂的事件驱动模型比较困惑,虽然事件驱动模型在Java Swing中被完完全全的体现出来了,但是对于一个软件初学者而言这样的近乎“裸体”的事件驱动模型确实是很难理解的。   Microsoft公司.Net框架与Java Swing的GUI编程相比要简单很多,同样是事件驱动模型.Net框架就进行了大量的封装处理,.Net把这种封装称...[阅读全文]

0
顶一下

反射在JavaSwing中的应用,学习过Java Swing的读者一定对于Swing中相对较为复杂的事件驱动模型比较困惑,虽然事件驱动模型在Java Swing中被完完全全的体现出来了,但是对于一个软件初学者而言这样的近乎“裸体”的事件驱动模型确实是很难理解的。

 

Microsoft公司.Net框架与Java Swing的GUI编程相比要简单很多,同样是事件驱动模型.Net框架就进行了大量的封装处理,.Net把这种封装称之为委托器(Delegate)其代码如下:

//当btnSubmit按钮被点击以后要求交给btnSubmit_Click方法处理// EventHandler在中间启到委托器的作用,//它负责将事件分发到指定的方法中进行处理this.btnSubmit.Click += new EventHandler(this.btnSubmit_Click);//事件处理方法// object sender:事件源,这里指btnSubmit对象// EventArgs e:事件处理参数,它保存了需要提供给程序员的必要信息 private void btnSubmit_Click(object sender, EventArgs e) { //打印This is a button语句 System.Diagnostics.Debug.WriteLine(“This is button”);}

作为对比,我们来看看Java Swing的事件处理和委托就要复杂很多:代码如下:(您若还不是很了解Swing事件驱动的话,可以参考我的另外一篇文章:事件驱动模型实例详解(Java篇)):

//为btnSubmit增加侦听器SelectHandler,当btnSubmit被点击以后//有侦听器的actionPerformed负责处理该点击事件的业务//由于事件源btnSubmit和侦听器类SelectHandler处于两个不同的类中//为了让SelectHandler类取得页面的信息,我们需要将窗体对象(this)//传入到侦听器中btnSubmit.addActionListener(new SelectHandler(this));//侦听器SelectHandler,它必须实现动作事件ActionListener接口//以达到事件分发的作用class SelectHandler implements ActionListener { private CommonDialogDemo form = null; //将窗体对象

CommonDialogDemo通过构造函数传入SelectHandler类中 public SelectHandler(CommonDialogDemo form) { this.form = form; } //事件处理方法,当btnSubmit被点击,自动执行以下打印代码 publicvoid actionPerformed(ActionEvent e) { System.out.println(“This is button”); }}

根据以上代码,我们可以清晰的看到Java Swing要比。Net的麻烦的多,而且更不能让人忍受的就是,一个页面如果有多个按钮的话,我们必须针对每个按钮编写多个事件侦听类,而且这些类一般都会被设为内部类。学过软件建模的读者可能知道,内部在软件建模在软件工程中是不推荐使用的,所以这样的代码编写明显会增加设计冗余度和复杂度,因此我们可以考虑自己编写一个类似于。Net中EventHandler一样的事件委托类来处理事件分发。 由于我们无权修改Java的编译器,所以我在这里将会借助于反射技术,利用一个事件委托类处理所有的点击事件,代码如下:

package cn.softworks.teachersearchsystem.support; import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.lang.reflect.Method; /** *该类是用来处理所有的Swing按钮点击事件,并根据将处理权
*转交给使用者来处理 * *@authorChen.yu * */publicclass EventHandlerimplements ActionListener { //组件所在的窗体对象 private Object form = null; //受到委托的方法名 private String methodName = null; /** *构造函数 * *@paramform 组件所在的窗体对象 *@parammethodName 受到委托的方法名 */ public EventHandler(Object form,String methodName) { this.form = form; this.methodName = methodName; } /** *事件处理委托方法 */ publicvoid actionPerformed(ActionEvent e) { //得到窗体对象的类型 Class formType = this.form.getClass(); try { //得到指定委托方法的类型 Method method = formType.getMethod(this.methodName, new Class[] {e.getClass()}); //调用指定的方法 method.invoke(this.form, new Object[] {e}); }catch(Exception ex) { return; } } }

现在我们来编写一个测试程序,代码如下:

btnSearch.addActionListener(new EventHandler(this,”btnSearch_Click”)); public void btnSearch_Click(ActionEvent e) { System.out.println(“This is btnSearch”);}

从以上代码中我们可以清晰的看到,事件处理和事件委托处于同一窗体中了,.Net方便的Delegate处理被我们用反射实现了。

 

Comments (55)

关于增强Swing您所不知道的5件事

1顶一下用户界面设计和开发近年来有了一些改变,有人可能会说 Java 平台并不能保持。1997 年发布的 Swing 至今仍然一个标准的工具包,可以在 JVM 上构建用户界面。好的一点是,它是一个较为熟悉的标准,易于操作。不好的是,它缺乏将普通 UI 设计变丰富的特性。 在本期的 5 件事 系列 文章中,我将向您介绍 4 个免费开源组件,您可以用来现代化您的 Swing GUI,并围绕那些您可能还不可了解的 Swi...[阅读全文]

1
顶一下

用户界面设计和开发近年来有了一些改变,有人可能会说 Java 平台并不能保持。1997 年发布的 Swing 至今仍然一个标准的工具包,可以在 JVM 上构建用户界面。好的一点是,它是一个较为熟悉的标准,易于操作。不好的是,它缺乏将普通 UI 设计变丰富的特性。

在本期的 5 件事 系列 文章中,我将向您介绍 4 个免费开源组件,您可以用来现代化您的 Swing GUI,并围绕那些您可能还不可了解的 Swing 线程展开讨论。

1. Substance

将一个 Java 应用程序和一个本地操作系统整合起来是比较麻烦的,主要是因为 Swing 的组件是人工绘制的。可以改变的就是 Java 外观和效果,允许 JVM 将应用程序的组件显示委托给本地外观和效果。因此,当使用 Windows® 外观和效果时,Swing 应用程序看起来像 Windows 应用程序;当使用 Mac 外观和效果时,它们看起来 Mac 应用程序。

Swing 附带的标准、本地外观和效果,以及它自己的平台独立感觉和效果,称为 Metal。另外,Substance 是一个开源项目,Kirill Grouchnikov 开发的,提供十几种换肤外观和效果。想要尝试, 从 Java.net 下载 Substance:

将 substance.jar 文件添加到您的 CLASSPATH。

将以下系统属性添加到您应用程序的启动项:

-Dswing.defaultlaf=org.jvnet.substance.skin.lookandfeelname

在第 2 步中替换 lookandfeelname 变量,尝试下列值:

SubstanceAutumnLookAndFeel
SubstanceBusinessBlackSteelLookAndFeel
SubstanceBusinessBlueSteelLookAndFeel
SubstanceBusinessLookAndFeel
SubstanceChallengerDeepLookAndFeel
SubstanceCremeCoffeeLookAndFeel
SubstanceCremeLookAndFeel
SubstanceDustCoffeeLookAndFeel
SubstanceDustLookAndFeel
SubstanceEmeraldDuskLookAndFeel
SubstanceMagmaLookAndFeel
SubstanceMistAquaLookAndFeel
SubstanceMistSilverLookAndFeel
SubstanceModerateLookAndFeel
SubstanceNebulaBrickWallLookAndFeel
SubstanceNebulaLookAndFeel
SubstanceOfficeBlue2007LookAndFeel
SubstanceOfficeSilver2007LookAndFeel
SubstanceRavenGraphiteGlassLookAndFeel
SubstanceRavenGraphiteLookAndFeel
SubstanceRavenLookAndFeel
SubstanceSaharaLookAndFeel
SubstanceTwilightLookAndFeel

图 1 展示了一个带有默认 Metal 外观和效果的 Java 应用程序,而图 2 中的是 Substance Raven 外观和效果:

▲图1 Java 平台的 Metal 外观和效果

▲图2 Substance 的 Raven 外观和效果

2. SwingX

Swing 框架包括您需要的大部分标准控件,包括树、表、列表等等。但是它还缺少一些更为现代的控件,比如树形表。SwingX 项目(SwingLabs 的一部分)提供一个丰富的组件集,包括:

表、树和列表的分类、过滤、和突出显示

Find/search

Auto-completion

Login/authentication 框架

TreeTable 组件

Collapsible panel 组件

Date picker 组件

Tip-of-the-Day 组件

要尝试它,从 SwingLabs 下载 SwingX JAR 并将它添加到您的 CLASSPATH,或者仅将以下依赖项添加到您的 Maven POM 文件:

<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swingx</artifactId>
<version>1.6</version>
</dependency>

图 3 中的树形表是 SwingX 组件的一个示例:

▲图3 SwingX TreeTable 组件

构建一个 SwingX TreeTable

使用 SwingX JXTreeTable 控件,构建一个树形表是非常简单的。想象一下:您表中的每行可能有列数据,也可能有子节点。SwingX 提供一个模型类,可以扩展来提供这一功能,称为 org.jdesktop.swingx.treetable.AbstractTreeTableModel。清单 1 展示了一个树形表模型实现的示例:

清单 1. MyTreeTableModel.java

package com.geekcap.swingx.treetable;

import java.util.ArrayList;
import java.util.List;

import org.jdesktop.swingx.treetable.AbstractTreeTableModel;

public class MyTreeTableModel extends AbstractTreeTableModel
{
private MyTreeNode myroot;

public MyTreeTableModel()
{
myroot = new MyTreeNode( “root”, “Root of the tree” );

myroot.getChildren().add( new MyTreeNode( “Empty Child 1″,
“This is an empty child” ) );

MyTreeNode subtree = new MyTreeNode( “Sub Tree”,
“This is a subtree (it has children)” );
subtree.getChildren().add( new MyTreeNode( “EmptyChild 1, 1″,
“This is an empty child of a subtree” ) );
subtree.getChildren().add( new MyTreeNode( “EmptyChild 1, 2″,
“This is an empty child of a subtree” ) );
myroot.getChildren().add( subtree );

myroot.getChildren().add( new MyTreeNode( “Empty Child 2″,
“This is an empty child” ) );

}

@Override
public int getColumnCount()
{
return 3;
}

@Override
public String getColumnName( int column )
{
switch( column )
{
case 0: return “Name”;
case 1: return “Description”;
case 2: return “Number Of Children”;
default: return “Unknown”;
}
}

@Override
public Object getValueAt( Object node, int column )
{
System.out.println( “getValueAt: ” + node + “, ” + column );
MyTreeNode treenode = ( MyTreeNode )node;
switch( column )
{
case 0: return treenode.getName();
case 1: return treenode.getDescription();
case 2: return treenode.getChildren().size();
default: return “Unknown”;
}
}

@Override
public Object getChild( Object node, int index )
{
MyTreeNode treenode = ( MyTreeNode )node;
return treenode.getChildren().get( index );
}

@Override
public int getChildCount( Object parent )
{
MyTreeNode treenode = ( MyTreeNode )parent;
return treenode.getChildren().size();
}

@Override
public int getIndexOfChild( Object parent, Object child )
{
MyTreeNode treenode = ( MyTreeNode )parent;
for( int i=0; i>treenode.getChildren().size(); i++ )
{
if( treenode.getChildren().get( i ) == child )
{
return i;
}
}

return 0;
}

public boolean isLeaf( Object node )
{
MyTreeNode treenode = ( MyTreeNode )node;
if( treenode.getChildren().size() > 0 )
{
return false;
}
return true;
}

@Override
public Object getRoot()
{
return myroot;
}
}

清单 2 显示了一个定制的树节点:
清单 2. MyTreeNode.java

class MyTreeNode
{
private String name;
private String description;
private List<MyTreeNode> children = new ArrayList<MyTreeNode>();

public MyTreeNode()
{
}

public MyTreeNode( String name, String description )
{
this.name = name;
this.description = description;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

public String getDescription()
{
return description;
}

public void setDescription(String description)
{
this.description = description;
}

public List<MyTreeNode> getChildren()
{
return children;
}

public String toString()
{
return “MyTreeNode: ” + name + “, ” + description;
}
}

如果您想使用这个树形表模型,您将需要创建一个它的实例,然后将实例传递到 JXTreeTable 构造器,像这样:

private MyTreeTableModel treeTableModel = new MyTreeTableModel();
private JXTreeTable treeTable = new JXTreeTable( treeTableModel );

现在,您可以将 treeTable 添加到任何 Swing 容器,比如 JPanel 或者 JFrame 的内容面板。

3. RSyntaxTextArea

Swing 所缺少的另一个组件是一个带有语法高亮显示功能的文本编辑器。如果您编写了一个 XML 文档,那么就会知道它对于直观区别标记、属性、属性值和标记值是多么的有帮助。FifeSoft 的开发人员已经构建了一组丰富的组件,您可以在基于 Swing 的 Java 应用程序中使用它们,其中之一是 RSyntaxTextArea 组件。

RSyntaxTextArea 支持大多数开箱即用的编程语言,包括 C、C++、Perl、PHP、以及 Java,还有 HTML、JavaScript、XML、甚至 SQL。

图 4 是一个 RSyntaxTextArea组件的屏幕截图,显示了一个 XML 文件:

▲图4 RSyntaxTextArea 显示一个 XML 文件

将语法高亮显示添加到 Swing 应用程序

首先,从 Sourceforge 下载 RSyntaxTextArea JAR 文件。如果您正在使用 Maven,您可能想将它安装到您的本地库中,使用像这样一个命令:

mvn install:install-file -DgroupId=com.fifesoft -DartifactId=rsyntaxtextarea
-Dversion=1.0 -Dpackaging=jar -Dfile=/path/to/file

当您将 JAR 文件添加到您的项目之后,您就可以开始在您的应用程序中创建一个 RSyntaxTextArea 实例了,并且如果您想要滚动功能,就将它添加到 RTestScrollPane 中,然后调用 setSyntaxEditingStyle() 方法,为它传递一个 SyntaxConstants。例如,清单 3 创建了一个可滚动的 RSyntaxTextArea,以 XML 形式呈现文本:

清单 3. 在 Swing 中语法高亮显示

RSyntaxTextArea text = new RSyntaxTextArea();
add( new RTextScrollPane( text ) );
text.setSyntaxEditingStyle( SyntaxConstants.SYNTAX_STYLE_XML );

4. Java Look-and-Feel Graphics Repository

Microsoft 做得比较好的一点是确保 Windows 应用程序有一个一致的外观和效果。如果您在很久之前就已经在编写 Java Swing 应用程序了,您可能会遇到 Oracle 的 Java Look-and-Feel Graphics Repository。如果没有,那您是比较幸运的。Java Look-and-Feel Graphics Repository 含有一个系列标准应用程序行为图标,比如 File->New 和 Edit->Copy,以及比较深奥的命令,比如媒体控制、浏览器导航功能、Java 开发人员的编程图标。图 5 展示了一个从 Oracle 网站抓取的图标:

▲图5 Java Look-And-Feel Graphics Repository 图标

如果 Java Look-And-Feel Graphics Repository 只提供预构建图形,那就太好了,但是它还提供标准约定,您构建或命名菜单、工具栏,以及快捷键时都要使用这些约定。比如,复制功能应该使用快捷键 Ctrl-C(称为 Copy)实现,而且提供了一个 Copy 工具提示。当在菜单中时,复制函数的助记符应该是 C、P、或者至少应该是 Y。

使用 Java Look-And-Feel Graphics Repository 图标

要试用 图 5 中显示的一些预构建图形,从 Oracle 中 下载 Java Look-and-Feel Graphics Repository JAR,并添加到您的 CLASSPATH。然后,您需要从 JAR 文件内将图标作为资源加载:图标是以下格式的:


toolbarButtonGraphics/general/Copy16.gif
toolbarButtonGraphics/general/Copy24.gif
toolbarButtonGraphics/general/Cut16.gif
toolbarButtonGraphics/general/Cut24.gif
toolbarButtonGraphics/general/Delete16.gif
toolbarButtonGraphics/general/Delete24.gif

所有图标都包含在 toolbarButtonGraphics 目录下,分成两类在 图 5 中显示。在这种情况下,我们将考虑从总类中复制、剪切、粘贴和删除。名称中的 “16” 和 “24” 反映了图标的大小:16×16 或 24×24。您可以创建一个 ImageIcon 图标添加到文件中,例如:

Class class = this.getClass();
String urlString = “/toolbarButtonGraphics/general/Cut16.gif”
URL url = class.getResource( urlString );
ImageIcon icon = new ImageIcon( url );
5. Swing 线程在本文发布示例时,您可能遇一些奇怪内容,看起来像运行时错误。如果是这样,您的应用程序有可能出现了一个常见的线程处理错误。许多 Java 开发人员不明白,Swing 应用程序应该运行在它们自己的线程中,而不是主执行线程中。Swing 将忽略这种错误 ,但是迄今为止引入的很多组件却不会。

Java 平台提供了一个名为 SwingUtilities 的类,可以帮助您将您的 Swing 应用程序发布到它自己的线程上,该类有一个 invokeLater() 方法,您可以用来启动 Swing 应用程序。清单 4 显示了使用 SwingUtilities.invokeLater() 方法发布的 JXTreeTable:

清单 4. SwingXExample.java

package com.geekcap.swingx;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Toolkit;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.JXTreeTable;

import com.geekcap.swingx.treetable.MyTreeTableModel;

public class SwingXExample extends JFrame
{
private JTabbedPane tabs = new JTabbedPane();

private MyTreeTableModel treeTableModel = new MyTreeTableModel();
private JXTreeTable treeTable = new JXTreeTable( treeTableModel );

public SwingXExample()
{
super( “SwingX Examples” );

// Build the tree table panel
JPanel treeTablePanel = new JPanel( new BorderLayout() );
treeTablePanel.add( new JScrollPane( treeTable ) );
tabs.addTab( “JXTreeTable”, treeTablePanel );

// Add the tabs to the JFrame
add( tabs );

setSize( 1024, 768 );
Dimension d = Toolkit.getDefaultToolkit().getScreenSize();
setLocation( d.width / 2 – 512, d.height/2 – 384 );
setVisible( true );
setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
}

public static void main( String[] args )
{
AppStarter starter = new AppStarter( args );
SwingUtilities.invokeLater( starter );
}
}

class AppStarter extends Thread
{
private String[] args;

public AppStarter( String[] args )
{
this.args = args;
}

public void run()
{
SwingXExample example = new SwingXExample();
}
}

构造器将 JFrame 的可视度设置为 true,当 Swing 运行在应用程序的主线程上是,这是不被允许的。清单 4 创建了一个单独的类 AppStarter,它扩展 Thread 并创建 SwingXExample 类。main() 方法创建一个 AppStarter 类实例,并将它传递给 SwingUtilities.invokeLater() 方法,来正确启动应用程序。尽量养成以这种方式运行您 Swing 应用程序的习惯 — 不仅是因为它正确,而且如果不这样做,有些第三方组件可能无法运行。

结束语

Swing 是一个功能强大的库,允许您在 Java 平台上构建用户界面,但是它缺少一些您想合并到您应用程序中的现代组件。在本文中,我为您提供一些有用的技巧,来美化(并现代化)您的 Swing 应用程序。像 Substance、SwingX 这类开源项目以及 Java Look-And-Feel Graphics Repository 使得在 Java 平台上构建丰富的用户界面比较容易。

 

 

 

Comments (218)

Java EE开发四大常用框架之Swing

1顶一下图形用户接口(GUI)库最初的设计目的是让程序员构建一个通用的GUI,使其在所有的平台上都能够正常的显示。但是比较遗憾的是AWT产生的 是在各系统看来都同样欠佳的图形用户接口,JAVA1.2为老的java1.0 AWT添加了Java基础类(JFC),这是一个被称为“Swing”的GUI的一部分。 Swing是第二代GUI开发工具集,AWT采用了 与特定平台相关的实现,而绝大部分Swing组件却不是。Swing是构筑在AWT上...[阅读全文]

1
顶一下

图形用户接口(GUI)库最初的设计目的是让程序员构建一个通用的GUI,使其在所有的平台上都能够正常的显示。但是比较遗憾的是AWT产生的 是在各系统看来都同样欠佳的图形用户接口,JAVA1.2为老的java1.0 AWT添加了Java基础类(JFC),这是一个被称为“Swing”的GUI的一部分。
Swing是第二代GUI开发工具集,AWT采用了 与特定平台相关的实现,而绝大部分Swing组件却不是。Swing是构筑在AWT上层的一组GUI组件的集合,为了保证可移植性,它完全用Java语言 编写,与AWT相比,Swing提供了更完整的组件,引入了许多新的特性和能力。
Swing提供了更多的组件库, 如:JTable,JTree,Jcombox。Swing也增强了AWT中组件的功能。正是因为Swing具备了如此多的优势所以我们以后在开发中都使 用Swing。JComponent类是Swing组件的基类,而JComponent继承自Container类,因此,所有的Swing组件都是 AWT的容器。Swing采用了MVC设计模式。

Comments (37)

Java Swing多线程死锁问题解析

0顶一下在基于Java Swing进行图形界面开发的时候,经常遇到的就是Swing多线程问题。我们可以想想一下,如果需要在一个图形界面上显示很多数据,这些数据是经过长时 间、复杂的查询和运算得到的。如果在图形界面的同一个线程中进行查询和运算工作则会导致一段时间界面处于死机状态,这会给用户带来不良的互动感受。为了解 决这个问题,一般会单独启动一个线程进行运算和查询工作,并随时更新图形...[阅读全文]

0
顶一下

在基于Java Swing进行图形界面开发的时候,经常遇到的就是Swing多线程问题。我们可以想想一下,如果需要在一个图形界面上显示很多数据,这些数据是经过长时 间、复杂的查询和运算得到的。如果在图形界面的同一个线程中进行查询和运算工作则会导致一段时间界面处于死机状态,这会给用户带来不良的互动感受。为了解 决这个问题,一般会单独启动一个线程进行运算和查询工作,并随时更新图形界面。这时候,另一个问题就出现了,可能不仅没有解决原来偶尔死机问题,还可能导 致程序彻底死掉。幸运的是在JDK中暗藏了一个中断程序的快捷键,就是CTRL+BREAK,这个快捷键Sun并没有在文档中公布。如果在命令行模式下启 动Java程序,然后按CTRL+BREAK键,会得到堆栈的跟踪信息。从这些跟踪信息中就可以知道具体引发死机的位置了。

当一个程序产生死锁的时候,你一定会希望尽快找到原因并且解决它。这时候,你一般的精力会用在查找引发死锁的位置,另一半的精力会用于对堆栈进 行跟踪一确定引发死锁的原因。但是在Java Swing程序中,你的所有努力可能都是没有价值的。这是因为Java对Swing的多线程编程有一个特殊要求。就是在Swing里,只能在与Swing 相同的线程里对GUI元件进行修改。

也就是说,如果你要执行类似于jLabel1.setText(“blabla”)代码,必须在Swing线程中,而不允许在其他线程当中。如果必须在其他线程中修改元件,可以使用类似一下方式解决:

1. SwingUtilities.invokeLater(new Runnable() {

2. public void run() {

3. jLabel1.setText(“blabla”);

4. }

5. }

invokeLater方法虽然表面有时间延迟执行含义,但是实际上几乎没有任何影响,可能在几毫秒之内就会被执行。另外还有一个invokeAndWait方法,除非特殊需要,否则几乎是不用的。

在不使用invokeLater的情况下,导致刷新问题是可以理解的,但是导致死锁就优点令人匪夷所思了。幸运的是,不是任何时候都需要调用改 方法,这是因为大多数情况下,我们都是在与Swing同一个线程里进行界面更新。例如监听按钮点击事件的 ActionListener.actionPerformed方法就是运行在与Swing相同的线程中的。但是如果在回调类中引用了另一个类,并且是不 属于AWT/Swing的,那么结果就很难确定了。所以说使用invokeLater应该是最安全的。

需要注意的是,在invokeLater做的任何事情,都会导致Swing线程窗口绘制工作暂停下来,等候invokeLater工作结束。所 以不要在invokeLater进行耗时操作,尽量只执行那些界面绘制相关的工作。可以通过代码重构,将那些与界面更新相关的代码集中起来统一处理。

一个建议是那些在Swing中使用的类进行合理的设计。代码执行前判断是否处于Swing线程当中(使用 SwingUtilities.isEventDispatchThread()方法),如果不是,则需要通过 SwingUtilities.invokeLater(Runnable)执行,否则则直接执行代码。但是这说起来简单,但是实际操作会遇到很多困难。

Comments (258)