swing外观美化,组件美化

jack_gogo 12年前

1. Substance

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

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

  1. 将 substance.jar 文件添加到您的 CLASSPATH
  2. 将以下系统属性添加到您应用程序的启动项:
    -Dswing.defaultlaf=org.jvnet.substance.skin.lookandfeelname

  3. 在第 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 外观和效果
有 Java 平台默认的 Metal 外观和效果的 GUI 的屏幕抓图。

图 2. Substance 的 Raven 外观和效果
有 Substance Raven 外观和效果的同一 GUI 的屏幕截图。

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 控件的 GUI 屏幕截图。

构建一个 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 文件
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 图标
RSyntaxTextArea 显示一个 XML 文件

如果 Java Look-And-Feel Graphics Repository 只提供预构建图形,那就太好了,但是它还提供标准约定,您构建或命名菜单、工具栏,以及快捷键时都要使用这些约定。比如,复制功能应该使用快捷键 Ctrl-C(称为 Copy)实现,而且提供了一个 Copy 工具提示。当在菜单中时,复制函数的助记符应该是 CP、或者至少应该是 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” 反映了图标的大小:16x16 或 24x24。您可以创建一个 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 平台上构建丰富的用户界面比较容易。参阅 参考资料 部分,了解关于这些项目和 Swing 编程的更多信息。


参考资料

学习

  • 关于...您不知道的 5 件事”:了解 Java 平台中您还有多少不知道的知识,这个系列专门将烦琐的 Java 技术变成非常有用的编程技巧。

  • Swing 简介”(Michael Abernethy,developerWorks,2005 年 6 月):这是一个两部分教程的第一部分,简要介绍了 Swing 库中的一些基础组件。

  • 关于 Maven 您不知道的 5 件事”(Steven Haines,developerWorks,2010 年 9 月):对于很多 Java 开发人员来说,Maven 是一个良好的构建工具,但是它也提供一个完整管理应用程序声明周期的工具集 — 其中几个在本文中做了介绍。

  • Swing threading and the event-dispatch thread”(John Zukowski,JavaWorld,2007 年 8 月):跟踪 Swing 的单线程事件模型,从 Java 1.0 到源头 JavaBeans 组件模型和 AWT 中,然后以一个常用的启动模式显示安装 bug,并显示如何修复它。

  • Substance (Swing) Sightings Volume 1”(Kirill Grouchnikov,Pushing Pixels,2007 年 8 月):选择有 Substance 皮肤的外观和效果的、由 Substance 创建者 Kirill Grouchnikov 编译的、正在运行的应用程序的屏幕截图。

  • Java Reference Guide:SwingX ”(Steven Haines,InformIT,2009 年 11 月):了解更多关于 SwingX 以及使用它来为 Java 应用程序 UI 构造树形表的信息。

  • developerWorks Java 技术专区:这里有数百篇关于 Java 编程每个方面的文章

获得产品和技术

  • 下载 Substance:扩展各种可以在您的 Swing 应用程序中实现的外观和效果。

  • 下载 SwingX:这个 SwingLabs 项目为 Swing UI 工具包带来了很多丰富的应用程序组件。

  • 下载 RSyntaxTextArea:您可以在您的基于 Swing 的 Java 应用程序中使用的丰富组件集合之一,由 FifeSoft 开发。

  • 下载 Java Look-and-Feel Graphics Repository:专用于 Java 外观和效果的一系列工具栏按钮图形,由 Oracle 的 Java Software Human Interface 小组开发。