レベル: 中級 Scott Davis, Founder, ThirstyHead.com
2009年 9月 29日 「実用的な Groovy」シリーズの今回の記事で Scott Davis が取り上げる話題は、サーバー・サイド Java™ の開発者の大部分が心から恐れる Swing です。この記事を読むとわかるように、Groovy の SwingBuilder を利用することで、強力ながら複雑な GUI フレームワークである Swing の難しさをいくらか軽減することができます。
私は最近、IBM developerWorks の連載記事「多忙な Java 開発者のための Scala ガイド」の著者である Ted Neward と対談しました (「参考文献」を参照)。私達はその対談の中で、彼がその連載の中で作成中の Scitter (Scala + Twitter) という興味深い Twitter ライブラリーについて議論しました。Scitter は Scala の Web サービス機能と XML の構文解析機能を利用していますが、この API のフロントエンドを作成することまではしないかもしれない、と彼は話していました。当然のことですが、私はそれを聞き、Twitter GUI を Groovy で作成したらどうなるだろうと考えたのです。Gwitter (Groovy + Twitter) なら、この GUI にふさわしいように思いませんか?
この記事では Scala + Groovy という統合の話題は取り上げませんが、この 2 つの言語を組み合わせて使うことで大きな相乗効果がもたらされる可能性があります。この記事では Scala + Groovy の代わりに、Java エコシステムの片隅にある、Java 開発者が見過ごしがちな Swing に注目します。しかしその前に、Groovy の XmlSlurper によって Twitter の Atom フィードを簡単に扱えることを説明します。
Twitter Search API
Twitter Search API のオンライン・ドキュメント (「参考文献」を参照) を見ると、Twitter に対する検索は単純な HTTP GET リクエストで行えるようです。クエリーはクエリー・ストリングの q パラメーターによって渡され、その結果は Atom (XML による配信フォーマット) または JSON (JavaScript Object Notation) のいずれかのフォーマットで返されます。つまり thirstyhead に関するすべてのつぶやきを集めた Atom の結果セットを得るためには、HTTP GET リクエストを http://search.twitter.com/search.atom?q=thirstyhead のように実行します。
リスト 1 を見るとわかるように、この結果は <feed> 要素の中に一連の <entry> 要素がネストされた形で返されます。
リスト 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 を作成し、構文解析し、容易に扱う」では、Groovy の XmlSlurper を使うと XML による結果を容易に処理できることを学びました。その記事で、XmlSlurper を使った結果がどのようなものになるかは紹介されているので、今度は 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 と入力すると、13 行から成る 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 クラスの作成が非常に難しいということはありません。しかもその成果として、Groovy クラスをコンパイルすることができ、そのコンパイルしたものを Java コードから呼び出すことができるのです。
例えば Tweet.groovy を作成してみます (リスト 4)。
リスト 4. Tweet.groovy
class Tweet{
String content
String published
String author
String toString(){
return "${author}: ${content}"
}
}
|
ご存知のように、この POGO (Plain Old Groovy Object) によって、これよりもはるかに冗長な POJO (Plain Old Java Object) を簡単に置き換えることができます。
今度はリスト 2 の検索スクリプトを 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[] が必要になるので、そのときのために、ここでは結果を Object[] にしています。
Search.groovy には main() メソッドがないことに注目してください。この状態で、この Search クラスをどう扱えばよいのでしょう。もちろん、ユニット・テストを使うのです。そこで、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 の操縦方法を学んでいるかのように思えるかもしれません。
Groovy の SwingBuilder を使っても、適切な LayoutManager を選択するとか、スレッドの問題を適切に処理するといった、タスクの持つ本質的な複雑さを軽減することはできません。しかし SwingBuilder を使うことで、構文の複雑さが軽減されるのです。このすぐ後に説明するように、名前付き引数または可変長引数を使った Groovy のコンストラクターは、さまざまな JComponent にとって最適です。JComponent はインスタンス化する必要があり、インスタンス化したら即座に一連のセッターを使って構成する必要があるからです。(SwingBuilder の詳細については「参考文献」を参照してください。)
しかし構文が簡単になることと同じくらい貴重なことは、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
リスト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
リスト 9 で、すべてのコンポーネント名から先頭の 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 を作成します。JMenuBar は JMenu を 1 つ以上含み、JMenu は 1 つ以上の JMenuItem を含みます。
Exit というメニュー項目を持つ File メニューを作成するために、リスト 11 のコードを Gwitter.groovy に追加します。
リスト 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 クロージャーがネストされた階層構造を持つことに注目してください。ここでは読みやすくするために customMenuBar を別に取り出していますが、これをフレームの中で、インラインで定義することも容易にできます。このクロージャーを定義できたら、そのクロージャーを frame クロージャーの中で呼び出します。再度 groovy Gwitter と入力し、File メニューが表示されることを確認します (図 4)。File > Exit の順に選択してアプリケーションを終了します。
図 4. Gwitter の File メニュー
リスト 11 を再度見てください。actionPerformed ハンドラーが、匿名クラスではなくクロージャーとして定義されていることに注目してください。こうすることで、Java の場合よりもコードがすっきりと読みやすくなったと思いませんか。
今度は、いくつかのフォーム要素を追加し、検索を実行できるようにしましょう。
検索パネルを追加する
経験豊富な Swing 開発者は、個々の JPanel から最終的なアプリケーションを簡単に作り上げます。JPanel というコンテナー・コンポーネントを使うと、関連する類似のコンポーネント同士を容易に一緒にまとめられるのです。
例えば Gwitter には、ユーザーが検索条件を入力するための JTextField と、リクエストを送信するための JButton が必要です。この 2 つのオブジェクトは 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 を追加する際には、searchPanel を NORTH、SOUTH、EAST、WEST、CENTER の領域のどこに表示するかを指定する必要があるということです。(方角がよくわからなくなってしまう人の場合には、PAGE_START、PAGE_END、LINE_START、LINE_END、CENTER を使うこともできます。) Swing で利用できる各種の LayoutManager については、「参考文献」を参照してください。
searchField 変数がクラス・レベルで宣言されていることに注意してください。これは、この変数に対してボタンなどの他のコンポーネントがアクセスできるようにするためです。それ以外のコンポーネントは匿名です。一連のクラスの内容を少し見ると、一部のコンポーネントが他のコンポーネントよりも重要であることがわかります。
おそらく読者の皆さんは、このボタンの actionPerformed リスナーが (まだ) 何もしていないことに、既にお気付きではないでしょうか。この時点では、このリスナーがする仕事は実際に何もありません。このリスナーを実装する前に、検索結果を保持する別のパネルをアプリケーションに追加する必要があります。
結果パネルを追加する
リスト 13 に示すように、searchPanel の場合と同じく、ネストされたクロージャーの中で resultsPanel を定義します。ただしこの場合には、パネルの中に JScrollPane という別のコンテナーをネストさせる必要があります。このコンポーネントによって、必要に応じて横スクロールバーと縦スクロールバーを表示したり非表示にしたりすることができます。Search.byKeyword() メソッドを呼び出した結果は、resultsList という名前の JList に表示されます。(JList.setListData() メソッドは Object[] を引数に取ります。まさにこの 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()
}
}
}
|
resultsList 変数が、searchField と同じように、クラス・レベルで定義されていることに注意してください。どちらの変数も、searchPanel の中にあるボタンの actionPerformed ハンドラーの中で使われます。
resultsPanel が追加されると、Gwitter が動作するようになります。コマンド・プロンプトで groovy Gwitter と入力し、想定どおりに動作することを確認します。thirstyhead を検索すると、図 5 のような結果が表示されるはずです。
図 5. 検索結果、その 1
ここで終了して成功を宣言することもできますが、勝利を宣言する前に、さらに 2 つの問題に対処したいと思います。第 1 に、検索ボタンの actionPerformed ハンドラーによって、知らないうちにスレッド化に関する悪夢が発生する可能性があります。第 2 に、このアプリケーションは実行結果が見づらくなっています。この 2 つの問題を次の 2 つのセクションで解決します。
EDT (イベント・ディスパッチ・スレッド)
Swing には、残酷で皮肉な側面があります。それは、ソフトウェア設計者のほうが適切に扱えるはずのマルチスレッド化に関する問題を、グラフィック・デザイナーが苦もなく扱うことができると想定している点、あるいはソフトウェア設計者がグラフィック・デザインの微妙なニュアンスやユーザビリティーの一般的な問題を理解できると想定している点です。
Swing アプリケーションでのスレッド化に関する問題のように複雑な問題は、いくつかの短い段落で説明しようとしても、とても説明しきれるものではありません。ここでは、Swing アプリケーションは基本的に、そのままの状態ではシングル・スレッドである、とだけ言っておきます。すべては EDT (イベント・ディスパッチ・スレッド) 上で行われます。ユーザーが Swing アプリケーションの動作の遅さや応答の悪さについて不満を言う場合、それは通常、経験の浅い開発者が、計算負荷が高く長々としたデータベース・クエリーや Web サービスの呼び出しを EDT 上で実行したためです。しかし EDT は画面の更新やメニューのクリックなどを処理するスレッドと同じスレッドなのです。ここではうかつにも、まさにこうした処理を検索ボタンの actionPerformed ハンドラーの中で実行してしまいました。(これを見ても、このよくありがちな誤りをいかに起こしやすいかがわかります。)
幸いなことに、javax.swing.SwingUtilities クラスには、適切な名前の付いた 2 つのコンビニエンス・メソッド (invokeAndWait() と invokeLater()) が用意されており、この 2 つのメソッドによってスレッド化の問題をいくらか軽減することができます、この 2 つのメソッドを使うと、EDT 上で同期または非同期にアクションを実行することができます。(SwingUtilities クラスについての詳細は「参考文献」を参照)。SwingBuilder を使うと、この 2 つのメソッドのいずれかを簡単に呼び出すことができます。しかも、このいずれかのメソッドを呼び出す方法に続く第 3 の方法として、SwingBuilder を使うことで、負荷の重そうなアクションを実行する新しいスレッドを苦労せずに作成することができます。
EDT 上で同期呼び出し (SwingUtilities.invokeAndWait()) を実行するためには、その呼び出しを edt{} クロージャーの中にラップします。EDT 上で非同期呼び出し (SwingUtilities.invokeLater()) を実行するためには、その呼び出しを doLater{} クロージャーの中にラップします。しかしここでは第 3 の方法を実行します。つまり、新しいスレッドを作成して 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 を使えばスレッド化に関する適切な処理を容易に行えるため、SwingBuilder を使わない理由はありません。
これでスレッド化に関する問題を解決できたので、残る作業は、このアプリケーションの実行結果を見やすくすることです。
リストを縞模様にする
現在の状態では Gwitter の実行結果が非常に見にくいことを認めざるを得ません。ここでは簡単なことを 2 つ行い、少しばかりHTML を使うことで見やすくします。幸いなことに、JLabel は基本的な HTML を描画することができます。Tweet.groovy の toString() メソッドを調整します (リスト 15)。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 を作成するように推奨しています。私はその助言を読みながら厳粛にうなずき、恥知らずにも、そのサンプル・コードをそのままコピーしました (完全な記事は「参考文献」を参照)。
Groovy の構文は Java の構文のスーパーセットであるため、その Java コードをそのまま Groovy ファイルにコピー・アンド・ペーストすることができました。もし私が Java コードと Groovy コードの両方をコンパイルする完全なビルド・システムを持っていたとしたら、その Java コードをそのまま Java ファイルのままに保持することができたはずです。しかしここではそのファイルに .groovy 拡張子を付けることで、コンパイルせずにすべての Gwitter コードを実行することができます。この場合も、Java 言語と Groovy とをシームレスに統合できることを利用しています。どのような Java ソリューションであれ、そのソリューションを何も変更せずに Groovy アプリケーションで使用することができるのです。
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 で利用することです。resultsPanel をリスト 17 のように調整します。
リスト 17. カスタムの CellRenderer を JList に追加する
def resultsPanel = {
swingBuilder.scrollPane(constraints: BorderLayout.CENTER){
//resultsList = list()
resultsList =
list(fixedCellWidth: 380, fixedCellHeight: 75, cellRenderer:new StripeRenderer())
}
}
|
最後にもう 1 度、コマンド・プロンプトから groovy Gwitter と入力します。そして thirstyhead を検索すると、図 6 のような結果が得られるはずです。
図 6. 縞模様付きで表示された結果
もっと長い時間をかけて Gwitter の見栄えを良くすることもできたのですが、約 50 行の Swing コード (もちろんサポート・クラスは別です) で実現できたことを見ると、きっと皆さんは驚くはずです。
まとめ
この記事で学んだように、Groovy によって Swing の本質的な複雑さを軽減することはできませんが、構文の複雑さを大幅に軽減することができます。そのおかげで、より重要な他の事項に専念できるようになります。
もし、この記事を読んで Groovy と Swing に対する興味が湧いたようであれば、ぜひ Griffon プロジェクトを調べてみてください (「参考文献」を参照)。このプロジェクトは Grails プロジェクトの構成に関するすべての scaffold と規約を提供していますが、SwingBuilder と Groovy の上に構築されており、Spring MVC や Hibernate の上に構築されているわけではありません。このプロジェクトはまだ初期の段階にありますが (この記事の執筆時点で最新リリースは 0.2 です)、十分に堅牢であり、JavaOne 2009 では Scriptg Bowl for Groovy 賞を獲得しました。そして、このプロジェクトに同梱されているサンプル・プロジェクトの 1 つが Greet であり、この Greet は Groovy で実装された完全な Twitter クライアントです。
次回は、Gwitter に明らかに欠けている機能である、新しいつぶやきを投稿する機能を追加します。その中で、基本的な HTTP 認証の処理方法、HTTP POST の実行方法、そして XmlSlurper に似ている ConfigSlurper の利用方法を学びます。では次回まで、皆さんが Groovy の実用的な使い方をたくさん見つけられることを祈っています。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Source code for article examples | j-groovy09299.zip | 24KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
- Groovy の最新の ZIP ファイルまたは tarball をダウンロードしてください。
議論するために
著者について
記事の評価
|