John Jiang

          a cup of Java, cheers!
          https://github.com/johnshajiang/blog

             :: 首頁 ::  :: 聯系 :: 聚合  :: 管理 ::
            131 隨筆 :: 1 文章 :: 530 評論 :: 0 Trackbacks
          你所不知道的五件事情--改進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編程的更多知識。

          posted on 2010-10-25 22:23 John Jiang 閱讀(4553) 評論(0)  編輯  收藏 所屬分類: JavaSE 、Java 、翻譯 、SwingGUI
          主站蜘蛛池模板: 西吉县| 壶关县| 霸州市| 武宣县| 随州市| 岑巩县| 集安市| 泸西县| 安阳市| 精河县| 安化县| 华容县| 合阳县| 石嘴山市| 太保市| 松桃| 临沧市| 洪湖市| 濉溪县| 应用必备| 霸州市| 宣汉县| 七台河市| 德令哈市| 都安| 莱芜市| 泰兴市| 乌拉特后旗| 蓬安县| 达拉特旗| 丰原市| 武冈市| 谢通门县| 灌南县| 兴安县| 托克托县| 长沙县| 平塘县| 荃湾区| 乌兰察布市| 景德镇市|