<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # 實戰 Groovy: SwingBuilder 和 Twitter API,第 1 部分 _構建基于 Swing 的 GUI 從未如此簡便_ 在這一期 [_實戰 Groovy_](http://www.ibm.com/developerworks/cn/java/j-pg/) 中,Scott Davis 要討論一個令大多數服務器端 Java? 開發人員畏懼的主題:Swing。Groovy 的 `SwingBuilder` 可以讓這個強大但復雜的 GUI 框架使用起來簡單一些。 我最近會見了 Ted Neward,他是 IBM developerWorks 文章系列 [_面向 Java 開發人員的 Scala 指南_](http://www.ibm.com/developerworks/cn/java/j-scala/) 的作者(見 [參考資料](#resources))。我們討論了他在這個系列中構建的一個有意思的 Twitter 庫,Scitter (Scala + Twitter)。Scitter 的重點在于 Scala 的 Web 服務和 XML 解析功能,Ted 承認他不太關心為這個 API 提供前端。當然,這啟發我考慮用 Groovy 編寫一個 Twitter GUI 會怎么樣?_Gwitter_ (Groovy + Twitter) 是個不錯的名字吧? 在本文中我不打算討論 Scala 和 Groovy 的集成,盡管在這兩種語言之間確實有許多協作的可能性。相反,我要討論 Java 領域中常常被 Java 開發人員忽視的一個主題:Swing。但是,在此之前,我先談談 Groovy 的 `XmlSlurper` 如何簡化 Twitter 的 Atom feed。 ## Twitter Search API 看一下 Twitter Search API 的在線文檔(見 [參考資料](#resources))。文檔表明可以通過發出簡單的 HTTP GET 請求搜索 Twitter。查詢通過查詢字符串中的 `q` 參數傳遞,結果以 Atom(一種 XML 聯合格式)或 JavaScript Object Notation (JSON) 的形式返回。因此,要想以 Atom 的形式得到所有提到 _thirstyhead_ 的條目,需要發出下面這樣的 HTTP GET 請求:`http://search.twitter.com/search.atom?q=thirstyhead`。 如清單 1 所示,返回的結果是嵌套在 `&lt;feed&gt;` 元素中的一系列 `&lt;entry&gt;` 元素: ##### 清單 1\. Twitter 搜索 Atom 結果 ``` <feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom"> <entry> <title>thirstyhead: New series from Andrew Glover: Java Development 2.0 http://bit.ly/bJX5i</title> <content type="html">thirstyhead: New series from Andrew Glover: Java Development 2.0 http://bit.ly/bJX5i</content> <id>tag:twitter.com,2007: http://twitter.com/thirstyhead/statuses/3419507135</id> <published>2009-08-20T02:54:54+00:00</published> <updated>2009-08-20T02:54:54+00:00</updated> <link type="text/html" rel="alternate" href="http://twitter.com/thirstyhead/statuses/3419507135"/> <link type="image/jpeg" rel="image" href="http://s3.amazonaws.com/twitter_production/profile_images/ 73550313/flame_normal.jpg"/> <author> <name>ThirstyHead.com</name> <uri>http://www.thirstyhead.com</uri> </author> </entry> <entry>...</entry> <entry>...</entry> <!-- snip --> </feed> ``` 在 “[實戰 Groovy:構建和解析 XML](http://www.ibm.com/developerworks/cn/java/j-pg05199/)” 中,可以看到很容易使用 Groovy 的 `XmlSlurper` 處理 XML 結果。既然了解了這些結果的形式,就來創建一個名為 searchCli.groovy 的文件,見清單 2: ##### 清單 2\. 解析 Atom 結果的 Groovy 腳本 ``` if(args){ def username = args[0] def addr = "http://search.twitter.com/search.atom?q=${username}" def feed = new XmlSlurper().parse(addr) feed.entry.each{ println it.author.name println it.published println it.title println "-"*20 } }else{ println "USAGE: groovy searchCli <query>" } ``` 在命令行上輸入 `groovy searchCli thirstyhead`,就會顯示簡潔的 Atom 結果,見清單 3: ##### 清單 3\. 運行 searchCli.groovy 腳本 ``` $ groovy searchCli thirstyhead thirstyhead (ThirstyHead.com) 2009-08-20T02:54:54Z New series from Andrew Glover: Java Development 2.0 http://bit.ly/bJX5i -------------------- kung_foo (kung_foo) 2009-08-18T12:33:32Z ThirstyHead interviews Venkat Subramaniam: http://blip.tv/file/2484840 "Groovy and Scala are good friends..." (via @mittie). very good. //snip ``` * * * ## 創建最初的 Gwitter 類 Groovy 腳本很適合編寫非正式的實用程序和證實概念,但是編寫 Groovy 類也不太困難。另外,可以編譯 Groovy 類并從 Java 代碼調用它們。 例如,可以編寫清單 4 所示的 Tweet.groovy: ##### 清單 4\. Tweet.groovy ``` class Tweet{ String content String published String author String toString(){ return "${author}: ${content}" } } ``` 這是一個 Plain Old Groovy Object (POGO),是非常復雜的 Plain Old Java Object (POJO) 的替代品。 現在,把 [清單 2](#listing2) 中的搜索腳本轉換為 Search.groovy,見清單 5: ##### 清單 5\. Search.groovy ``` class Search{ static final String addr = "http://search.twitter.com/search.atom?q=" static Object[] byKeyword(String query){ def results = [] def feed = new XmlSlurper().parse(addr + query) feed.entry.each{entry-> def tweet = new Tweet() tweet.author = entry.author.name tweet.published = entry.published tweet.content = entry.title results << tweet } return results as Object[] } } ``` 通常情況下,我會讓結果保持 `java.util.ArrayList` 的形式。但是,本文后面使用的 `javax.swing.JList` 需要一個 `Object[]`,所以這里提前做一些準備。 注意,我在 Search.groovy 中去掉了 `main()` 方法。現在如何與這個類交互呢?當然可以通過單元測試!創建 SearchTest.groovy,見清單 6: ##### 清單 6\. SearchTest.groovy ``` class SearchTest extends GroovyTestCase{ void testSearchByKeyword(){ def results = Search.byKeyword("thirstyhead") results.each{ assertTrue it.content.toLowerCase().contains("thirstyhead") || it.author.toLowerCase().contains("thirstyhead") } } } ``` 如果在命令提示上輸入 `groovy SearchTest`,然后看到 `OK (1 test)`(見清單 7),就說明已經成功地把搜索腳本轉換為可重用的類了: ##### 清單 7\. 成功測試的運行結果 ``` $ groovy SearchTest . Time: 4.64 OK (1 test) ``` 現在底層基礎結構已經就位了,下一步是開始為它提供漂亮的前端。 * * * ## `SwingBuilder` 簡介 Swing 是一個極其強大的 GUI 工具集。但糟糕的是,有時候其復雜性會影響開發人員揮發它的能力。如果您剛接觸 Swing,會覺得像是在學習開波音 747,而您實際上只需要開單引擎的 Cessna 或滑翔機。 Groovy 的 `SwingBuilder` 并不能降低各種任務內在的復雜性,比如選擇適當的 `LayoutManager` 或處理線程問題。它降低的是語法復雜性。Groovy 的命名參數/變量參數構造器非常適合需要實例化的各種 `JComponent`,然后馬上可以為它們配置一系列設置器。(關于 `SwingBuilder` 的更多信息,請參見 [參考資料](#resources))。 但是,同樣有價值的是 Groovy 對閉包的使用。對于 Swing,我長期關注的問題是自然的層次結構似乎在實現細節中消失了。在 Java 代碼中,會得到一組相互脫節的組件,看不出哪個組件屬于哪個組件。可以以任意次序聲明 `JFrame`、`JPanel` 和 `JLabel`。在代碼中,它們看起來是平等的;但是,實際上 `JFrame` 包含 `JPanel`,`JPanel` 進而包含 `JLabel`。清單 8 給出一個示例: ##### 清單 8\. HelloJavaSwing.java ``` import javax.swing.*; public class HelloJavaSwing { public static void main(String[] args) { JPanel panel = new JPanel(); JLabel label = new JLabel("Hello Java Swing"); JFrame frame = new JFrame("Hello Java Swing"); panel.add(label); frame.add(panel); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(200,300); frame.setVisible(true); } } ``` 編譯這段代碼 (`javac HelloJavaSwing.java`) 并運行它 (`java HelloJava`),應該會顯示圖 1 所示的應用程序: ##### 圖 1\. `HelloJavaSwing` ![](https://box.kancloud.cn/2016-05-11_5732f381081f3.jpg) 清單 9 給出用 Groovy 編寫的同一個應用程序。可以看到 `SwingBuilder` 使用了閉包,這讓我們可以清晰地看出擁有關系鏈。 ##### 清單 9\. HelloGroovySwing.groovy ``` import groovy.swing.SwingBuilder import javax.swing.* def swingBuilder = new SwingBuilder() swingBuilder.frame(title:"Hello Groovy Swing", defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[200,300], show:true) { panel(){ label("Hello Groovy Swing") } } ``` 輸入 `groovy HelloGroovySwing` 會看到圖 2 所示的應用程序: ##### 圖 2\. `HelloGroovySwing` ![](https://box.kancloud.cn/2016-05-11_5732f38117f87.jpg) 注意,在 [清單 9](#listing9) 中,所有組件名去掉了開頭的 `J`,方法名中也去掉了多余的 `get` 和 `set`。接下來,注意 `frame` 的命名參數構造器。在幕后,Groovy 調用無參數構造器,然后調用設置器方法,這與前面的 Java 示例沒有區別。但是,設置器方法都集中在構造器中,代碼更簡潔了,去掉 `set` 前綴和末尾的圓括號也大大減少了視覺干擾。 如果您不了解 Swing,這段代碼看起來可能仍然比較復雜。但是,如果您具備哪怕最粗淺的 Swing 經驗,就可以看出它具有 Swing 的特征:干凈、清晰和高效。 正如在前一節中所做的,通過腳本了解概念,然后把腳本轉換為類。創建文件 Gwitter.groovy,見清單 10。這是 Groovy + Twitter 客戶機 UI 的起點。 ##### 清單 10\. Gwitter UI 的骨架 ``` import groovy.swing.SwingBuilder import javax.swing.* import java.awt.* class Gwitter{ static void main(String[] args){ def gwitter = new Gwitter() gwitter.show() } void show(){ def swingBuilder = new SwingBuilder() swingBuilder.frame(title:"Gwitter", defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[400,500], show:true) { } } } ``` 輸入 `groovy Gwitter`,確認會出現空的框架。如果一切正常,下一步是在應用程序中添加一個簡單的菜單。 * * * ## 添加菜單欄 在 Swing 中創建菜單提供另一個具有自然層次結構的組件示例。創建一個 `JMenuBar`,它包含一個或多個 `JMenu`,`JMenu` 進而包含一個或多個 `JMenuItem`。 為了創建包含 `Exit` 菜單項的 `File` 菜單,在 Gwitter.groovy 中添加清單 11 中的代碼: ##### 清單 11\. 在 Gwitter 中添加 `File` 菜單 ``` import groovy.swing.SwingBuilder import javax.swing.* import java.awt.* class Gwitter{ static void main(String[] args){ def gwitter = new Gwitter() gwitter.show() } void show(){ def swingBuilder = new SwingBuilder() def customMenuBar = { swingBuilder.menuBar{ menu(text: "File", mnemonic: 'F') { menuItem(text: "Exit", mnemonic: 'X', actionPerformed: { dispose() }) } } } swingBuilder.frame(title:"Gwitter", defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[400,500], show:true) { customMenuBar() } } } ``` 請注意 `customMenuBar` 閉包的嵌套層次結構。為了便于閱讀,這里添加了換行和縮進,但是同樣很容易在同一行中定義它。定義這個閉包之后,在 `frame` 閉包中調用它。再次輸入 `groovy Gwitter`,確認會出現 `File` 菜單,見圖 4。選擇 **File &gt; Exit**,關閉這個應用程序。 ##### 圖 4\. Gwitter 的 File 菜單 ![](https://box.kancloud.cn/2016-05-11_5732f3812a910.jpg) 再看看 [清單 11](#listing11)。注意,`actionPerformed` 處理函數定義為閉包,而不是匿名類。與相應的 Java 代碼相比,這樣的代碼更干凈、更容易閱讀。 現在,添加一些表單元素以執行搜索。 * * * ## 添加搜索面板 經驗豐富的 Swing 開發人員善于用單獨的 `JPanel` 組裝出最終的應用程序。這些容器組件可以方便地把相似、相關的組件分組在一起。 例如,Gwitter 需要一個 `JTextField`(讓用戶能夠輸入搜索條件)和一個 `JButton` (用于提交請求)。把這兩個組件分組在一個 `searchPanel` 閉包中是有意義的,見清單 12: ##### 清單 12\. 添加搜索面板 ``` import groovy.swing.SwingBuilder import javax.swing.* import java.awt.* class Gwitter{ def searchField static void main(String[] args){ def gwitter = new Gwitter() gwitter.show() } void show(){ def swingBuilder = new SwingBuilder() def customMenuBar = { swingBuilder.menuBar{ menu(text: "File", mnemonic: 'F') { menuItem(text: "Exit", mnemonic: 'X', actionPerformed: {dispose() }) } } } def searchPanel = { swingBuilder.panel(constraints: BorderLayout.NORTH){ searchField = textField(columns:15) button(text:"Search", actionPerformed:{ /* TODO */ } ) } } swingBuilder.frame(title:"Gwitter", defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[400,500], show:true) { customMenuBar() searchPanel() } } } ``` 開始處理面板之后,就要選擇適當的 `LayoutManger`。在默認情況下,`JPanel` 使用 `FlowLayout`。這意味著 `textField` 和 `button` 挨著水平排列。 `JFrame` 的 `contentPane` 不太一樣 — 它在默認情況下使用 `BorderLayout`。這意味著在框架中添加 `searchPanel` 時需要指定它應該出現在哪個區域:`NORTH`、`SOUTH`、`EAST`、`WEST` 或 `CENTER`。(如果您的地理知識實在糟糕,也可以使用 `PAGE_START`、`PAGE_END`、`LINE_START`、`LINE_END` 和 `CENTER`)。關于 Swing 中可用的各種 `LayoutManager` 的更多信息,請參見 [參考資料](#resources)。 注意,`searchField` 變量是在類級聲明的。因此,按鈕等其他組件也可以訪問它。其他組件都是匿名的。快速瀏覽一下類屬性,就會看出某些組件比較重要。 您可能已經注意到按鈕的 `actionPerformed` 監聽器目前沒有做任何事情。現在實際上還不需要它做什么。在實現它之前,需要在應用程序中添加另一個面板:用來顯示搜索結果的面板。 * * * ## 添加結果面板 如清單 13 所示,像對待 `searchPanel` 那樣,在嵌套的閉包中定義 `resultsPanel`。但是,這一次在這個面板中嵌套另一個容器:`JScrollPane`。這個組件可以根據需要顯示和隱藏水平和垂直滾動條。`Search.byKeyword()` 方法調用的結果顯示在名為 `resultsList` 的 `JList` 中。(`JList.setListData()` 方法接受一個 `Object[]`— 這就是 `Search.byKeyword()` 方法返回的結果)。 ##### 清單 13\. 添加 `resultsPanel` ``` import groovy.swing.SwingBuilder import javax.swing.* import java.awt.* class Gwitter{ def searchField def resultsList static void main(String[] args){ def gwitter = new Gwitter() gwitter.show() } void show(){ def swingBuilder = new SwingBuilder() def customMenuBar = { swingBuilder.menuBar{ menu(text: "File", mnemonic: 'F') { menuItem(text: "Exit", mnemonic: 'X', actionPerformed: {dispose() }) } } } def searchPanel = { swingBuilder.panel(constraints: BorderLayout.NORTH){ searchField = textField(columns:15) button(text:"Search", actionPerformed:{ resultsList.listData = Search.byKeyword(searchField.text) } ) } } def resultsPanel = { swingBuilder.scrollPane(constraints: BorderLayout.CENTER){ resultsList = list() } } swingBuilder.frame(title:"Gwitter", defaultCloseOperation:JFrame.EXIT_ON_CLOSE, size:[400,500], show:true) { customMenuBar() searchPanel() resultsPanel() } } } ``` 注意,與 `searchField` 一樣,`resultsList` 變量是在類級定義的。`searchPanel` 中按鈕的 `actionPerformed` 處理函數使用這兩個變量。 添加 `resultsPanel` 之后,Gwitter 現在有實際功能了。在命令提示上輸入 `groovy Gwitter`,檢查它是否工作正常。搜索 _thirstyhead_ 應該會產生圖 5 所示的結果: ##### 圖 5\. 搜索結果 ![](https://box.kancloud.cn/2016-05-11_5732f3813ef18.jpg) 現在可以宣布成功了,但是我想先解決兩個問題。第一個問題是搜索按鈕的 `actionPerformed` 處理函數可能會引起線程問題。另一個問題是這個應用程序太一般了。下面兩節解決這些問題。 * * * ## 事件分派線程 Swing 的缺點在于,它期望圖形設計師能夠應付多線程問題,而這是應該由軟件工程師處理的,或者期望軟件工程師理解圖形設計和易用性問題。 我不可能在短短幾段文字中討論 Swing 應用程序中的線程問題這么復雜的主題。只需指出基本的 Swing 應用程序本質上是單線程的。所有活動都在事件分派線程 (EDT) 上進行。當用戶抱怨 Swing 應用程序反應遲緩或完全沒有反應時,往往是因為某個開發新手在 EDT 上執行長時間的計算密集型的數據庫查詢或 Web 服務調用 — 這個線程也負責處理屏幕刷新、菜單單擊等。我們無意中在搜索按鈕的 `actionPerformed` 處理函數上犯了同樣的錯誤。(您可以看出多么容易犯這種錯誤)。 好在 `javax.swing.SwingUtilities` 類提供了幾個方便的方法 — `invokeAndWait()` 和 `invokeLater()`,它們可以消除某些線程問題。可以使用這兩個方法在 EDT 上同步或異步地執行操作。(關于 `SwingUtilities` 類的更多信息見 [參考資料](#resources))。`SwingBuilder` 讓我們很容易調用這兩個方法,還提供了第三個選擇:可以簡便地生成新線程以執行處理時間長的操作。 要想在 EDT 上執行同步調用 (`SwingUtilities.invokeAndWait()`),可以把調用放在 `edt{}` 閉包中。要想在 EDT 上執行異步調用 (`SwingUtilities.invokeLater()`),就把調用放在 `doLater{}` 閉包中。但是,我想讓您體驗一下第三個選擇:生成新線程來處理 `Search.byKeyword()` 方法調用。為此,需要把代碼放在 `doOutside{}` 閉包中,見清單 14: ##### 清單 14\. 使用 `doOutside` 閉包 ``` def searchPanel = { swingBuilder.panel(constraints: BorderLayout.NORTH){ searchField = textField(columns:15) button(text:"Search", actionPerformed:{ doOutside{ resultsList.listData = Search.byKeyword(searchField.text) } } ) } } ``` 在像 Gwitter 這樣簡單的應用程序中,在 EDT 上執行 Web 服務調用很可能沒什么不好的效果。但是,如果把這樣的代碼拿給 Swing 專家看,他們會用鄙視的目光看您,就像是您在快車道里慢慢地開車,或者把車停在商店停車場的殘疾人專用車位上了。因為通過使用 `SwingBuilder` 很容易正確地處理線程,完全沒有理由不這么做。 既然解決了線程問題,下面就讓這個應用程序更漂亮一些。 * * * ## 給列表增加條紋效果 坦率地說,Gwitter 目前很難看。我要使用一些 HTML 代碼做兩個簡單的改進,讓外觀和感覺好一些。`JLabel` 可以顯示基本的 HTML。按清單 15 調整 Tweet.groovy 的 `toString()` 方法。`JList` 調用 `toString()` 方法顯示結果。 ##### 清單 15\. 在 `toString()` 方法中返回 HTML ``` class Tweet{ String content String published String author String toString(){ //return "${author}: ${content}" return """<html> <body> <p><b><i>${author}:</i></b></p> <p>${content}</p> </body> </html>""" } } ``` 下一個改進略微有點復雜。一種常用的 GUI 技巧是給長的列表或表格加上條紋效果。用不同的顏色顯示奇數行和偶數行,這樣讀者更容易閱讀。我在搜索引擎中搜索了 _JList stripes_,采納了找到的第一篇文章中的建議。作者建議創建一個定制的 `DefaultListCellRenderer`。我完全贊同他的意見并按原樣借用他的示例代碼(完整的文章見 [參考資料](#resources))。 因為 Groovy 語法是 Java 語法的超集,所以可以把 Java 代碼復制到 Groovy 文件中,不需要修改。如果有功能全面的構建系統,可以編譯 Java 和 Groovy 代碼,那么只需把這段代碼留在 Java 文件中。但是,通過把代碼文件的擴展名改為 .groovy,我可以運行所有未編譯的 Gwitter 代碼。我再次利用了 Java 語言和 Groovy 之間的無縫集成。可以在 Groovy 應用程序中不加修改地使用任何 Java 解決方案。 創建文件 StripeRenderer.groovy,添加清單 16 中的代碼: ##### 清單 16\. 創建有條紋效果的 `CellRenderer` ``` import java.awt.*; import javax.swing.*; class StripeRenderer extends DefaultListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); if(index%2 == 0) { label.setBackground(new Color(230,230,255)); } label.setVerticalAlignment(SwingConstants.TOP); return label; } } ``` 有了 `StripeRenderer` 類之后,最后需要讓 `JList` 使用它。按清單 17 調整 `resultsPanel`: ##### 清單 17\. 在 `JList` 中添加定制的 `CellRenderer` ``` def resultsPanel = { swingBuilder.scrollPane(constraints: BorderLayout.CENTER){ //resultsList = list() resultsList = list(fixedCellWidth: 380, fixedCellHeight: 75, cellRenderer:new StripeRenderer()) } } ``` 在命令提示上再次輸入 `groovy Gwitter`。搜索 _thirstyhead_ 應該會產生圖 16 所示的結果: ##### 圖 6\. 有條紋效果的結果 ![](https://box.kancloud.cn/2016-05-11_5732f3815ddd5.jpg) 我可以花更多時間美化 Gwitter 的外觀和感覺,但是我希望您對大約 50 行 Swing 代碼(當然不包括支持類)所實現的效果印象深刻。 * * * ## 結束語 正如本文中指出的,Groovy 并不能降低 Swing 內在的復雜性,但是它可以顯著降低語法復雜性。這讓您能夠留出時間應付更重要的問題。 如果本文引起了您對 Groovy 和 Swing 的興趣,您應該好好研究一下 Griffon 項目(見 [參考資料](#resources))。它提供許多優于 Grails 項目的功能和慣例,但是它基于 `SwingBuilder` 和 Groovy 而不是 Spring MVC 和 Hibernate。這個項目仍然處于早期階段(到編寫本文時最新版本是 0.2),但是它已經很出色了,在 JavaOne 2009 上贏得了 Scripting Bowl for Groovy。另外,它提供的示例項目之一是 Greet,這是一個用 Groovy 實現的完整的 Twitter 客戶機。 下一次,我將在 Gwitter 中添加一些必備特性:發布新 Tweet 的功能。在此過程中,您將學習如何處理基本的 HTTP 身份驗證、執行 HTTP POST 以及使用與 `XmlSlurper` 相似的 `ConfigSlurper`。在此之前,我希望您探索應用 Groovy 的各種可能性。 * * * ## 下載 | 描述 | 名字 | 大小 | | --- | --- | --- | | 文章示例的源代碼 | [j-groovy09299.zip](http://www.ibm.com/developerworks/apps/download/index.jsp?contentid=447453&filename=j-groovy09299.zip&method=http&locale=zh_CN) | 24KB |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看