• 你所不知道的五件事情改进Swing(转)


    你所不知道的五件事情--改进Swing

    这是Ted NewardIBM developerWorks5 things系列文章中的一篇,讲述了关于改进Swing应用的一些窍门,值得大家学习。(2010.10.25最后更新)

    摘要:Swing已是一个比较老的工具集了,在美观的用户界面出来之前需要开发很长时间。它缺少一些你在开发富UI时所需的组件。幸运地是,像 Substance,SwingX及Java Look-and_Feel图形仓库这样的开源项目使这一切变得不同。作者Steven Haines向你展示了如何无痛苦地向你的Swing UI中添加树表,语法高亮,以及其它更多的东西。

        在最近这些年里,用户界面设计与开发已经发生了很大的改变,一些人可能会说Java平台已经停滞不前了。发布于1997年的Swing仍然是在JVM中构建用户界面的标准工具包。从好的方面说,相似的标准便于协作;从坏的方面说,它缺少富UI设计中已经普遍存在的特性。
    在本期的5 things系列中,我会介绍四个免费的开源组件,你能用它们使Swing GUI更时髦。然后,我们所讨论的内容将围绕着你所不知道的Swing线程。

    1. Substance
        将Java应用程序与本地操作系统进行整合是困难的,主要是因为Swing要手工绘制它自己的组件。解决该问题的权宜之计之一就是Java外观,它允许JVM将应用程序的组件外观代理成本地外观;当使用Mac外观时,它们看起来就是像是Mac应用。
        Swing提供标准的本地外观,也提供它自己的独立于平台的外观,叫作Metal。另外,Kirill Grouchnikov开发的Substance是一个开源的项目,它提供了更多的外观皮肤。要想尝试一下,可以从java.net下载Substance,然后:
    1. 将substance.jar文件加到你的CLASSPATH中。
    2. 将下面的系统配置加到应用程序中的启动脚本中:

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


    3. 在第二步中,对于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的一部分,提供了一个富组件集,包括如下:
        * Sorting, filtering, and highlighting for tables, trees, and lists
        * Find/search
        * Auto-completion
        * Login/authentication framework
        * TreeTable component
        * Collapsible panel component
        * Date picker component
        * Tip-of-the-Day component

    要尝试的话,从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树表
    使用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 0return "Name";
            
    case 1return "Description";
            
    case 2return "Number Of Children";
            
    defaultreturn "Unknown";
            }
        }

        @Override
        
    public Object getValueAt( Object node, int column )
        {
            System.out.println( 
    "getValueAt: " + node + "" + column );
            MyTreeNode treenode 
    = ( MyTreeNode )node;
            
    switch( column )
            {
            
    case 0return treenode.getName();
            
    case 1return treenode.getDescription();
            
    case 2return treenode.getChildren().size();
            
    defaultreturn "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;
            
    forint 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. Swing中的语法高亮

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


    4. Java外观图形仓库
        Microsoft作的很好的工作之一就是确保Windows应用都有着一致的外观。如果你已经编写过一个Java Swing应用,无论用了多长时间,你可能已经访问过Oracle的Java外观图形仓库。如果没有,你会对它感满意的。Java外观图形仓库创建一组针对标准应用行为的图标,例如File->New和Edit->Copy,还有更多的鲜为人知的命令,如媒体控件,浏览器导航功能,以及针对 Java开发员的编程工作。图5展示了一个从Oracle网站上获取的图标的截屏:

    图5. Java外观图形仓库图标


    如果Java外观图形仓库只是提供预置的图形,它也足够好了,但它还提供了当你在构建和命名菜单,菜单栏,以及快捷键的标准规范。例如,复制功能应该有Ctrl-C快捷键,命名为Copy,并给一个Copy的提示。当它在菜单中,复制功能的助记符应为C,P,或至少是Y。

    使用Java外观图形仓库的图标
    尝试图5所示的一些预置图形,要从Oracle网站上下载Java外观图形仓库的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线程
        当启动文中示例时,你可能会遇到一些看起来奇怪的运行时错误。如果是这样,在你的Swing应用中,你可能会犯一个通常的线程错误。许多Java开发者不知道Swing应用程序希望运行在它们自己的线程中,而不是运行在主运行线程中。Swing不会原谅这方面的错误,但介绍过的许多组件目前还不会这样。
    为了帮助你在Swing应用自己的线程中启动它自己,Java平台提供了一个叫作SwingUtilties的类,它有一个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( 
    1024768 );
            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是不允许这么做的。所以清单创建一个独立的类,叫作 AppStarter,它继承自Thread并会创建SwingXExample类的实例。main()方法创建AppStarter类的一个实例,并将它传给SwingUtilities.invokeLater()方法以方便启动应用。尝试着养成这样的习惯去运行Swing应用--不仅因为这是正确的方式,也因为如果你不这么做一些第三方的组件将无法工作。

    结论
        Swing是一个强大的类库,它允许你在Java平台上构建用户界面,但它缺少一些你可能想引入的现代的组件。在本文中,为了美化(以及现代化)你的 Swing应用,我提供了一些小窍门。开源项目,如Substance,SwingX以及Java外观图形仓库使在Java平台上构建富用户界面变得更容易。查看资源章节,以学习关于这些开源项目以及Swing编程的更多知识。

  • 相关阅读:
    Android编译系统环境过程初始化分析【转】
    Android内核开发:理解和掌握repo工具【转】
    QQ空间如何设置被删除的好友不能访问空间
    用简单的C语言实现多任务轮流切换(模拟操作系统线程机制)【转】
    可重入函数与不可重入函数【转】
    关于链表中头指针和头结点的理解【转】
    C语言中static的使用方法【转】
    指针与地址的区别【转】
    柔性数组【转】
    void及void指针介绍【转】
  • 原文地址:https://www.cnblogs.com/hualidezhuanshen/p/3132464.html
Copyright © 2020-2023  润新知