watermint.org

Takayuki Okazaki's blog

Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - java-ja 第十二回 第1回チキチキ そろそろ Swing の実力をおまえらにみせてやんよ(仮)

11月21日はjava-ja第十二回。ちなみに、java-jaのイベントは第N回というように採番されていますが、実際には小数点があったり、実施される順序は必ずしも数字が小さい順ではないなど、表記揺れがあるので最新のイベントがどれなのかを知るには少し工夫が要るかもしれません。さて、この日は六本木にあるGREEさんのセミナールームでのjava-ja。六本木は絶対に立体迷路だと思います。元々少し遅れそうだったんですが、六本木一丁目駅に着いてからは少なくとも30分六本木をさまよいました。その原因はKYUKAさんもかかれていますが、すくなからずとも、GREEさんの会社案内デザインに因ると思います。
map.gif
こちらがGREEさんの案内図。僕は六本木一丁目駅の出口1に出れば、あとはこの地図通りに進めばよいはずでした。
六本木一丁目駅 出口1
一方こちらは東京メトロによる六本木一丁目駅の周辺地図です。僕はこちらの一番出口と書いてある方角に出ました。もう一度GREEさんの案内図と比べていただくと分かりますが、GREEさんの地図では、ぱっと見たところ、六本木ティーキューブの出口がさも出口1かのように見えますが、実際の出口はそれを通過しない、隣にIBMの建物が見える側です。このため、行けども行けども六本木4丁目という住所が見あたらない・・・。人目をはばからずダウンジャケットを抱えて、ぴちぴちのTシャツで六本木を歩き回りました。六本木を上記の情報でしか知らない僕としては、GREEさんの案内図は次のようにあってほしいなと思います。
map.png
ちなみに迷った30分の中には出口1にたどり着くためにかかった時間も含まれていますので、大遅刻の原因がGREEさんのせいではないことを付け加えておきます(99%僕に方向感覚が無いのが原因です)

目標のビルについてからも、全くビルが開いている気配がなかったんですが、java-jaこっち表示に勇気づけられようやく会場にたどり着けました。

会場にたどり着くと、太一さんがすでに発表中で、入ったとたんに何か話を振られてしまいましたが、空気が読めず気まずい感じに(どう答えるのが正解だったんだろう・・)

続いて小野さんのプレゼン。デザインパターンの話とかすごく良かったんですが、それよりも続々と届くケータリングが気になって話に集中できません。

あと、ymsr先生のチョイスだそうですが、会場に用意されたビールにGuiness!

すばらしい!なんて思いながら小野さんのプレゼンの間に2〜3本いただきました。す、すいません。

集金の列。

続いては山浦さんのレイアウトについての発表。参考になったのはある種の割り切り方です。Swingで悪名高いクラスといえば、扱いが複雑なGridBagLayoutですが、それをHTMLのTable相当に利用することに徹することで、うまく利便性と表現のバランスが保たれています。僕の方眼紙コンポーネントでも是非参考にしたいところです。

続いては櫻庭さんのSwing Synth。いつもながら櫻庭さんのプレゼンのクオリティーの高さには感心します。

Swingとは関係ありませんが (^^;; 、大人の科学 シンセサイザークロニクルでの演奏。D90のDムービーでも演奏を収録したんですが、とっさに構えたら縦位置で、うまくYouTube等にあげるようにまだ編集できていませんのでまた今度・・。縦横かえるだけでも意外と面倒なモンですね・・・。Photoshop Extended CS3ぐらいしか手元じゃ縦横変換できるソフトないし・・

僕の発表はかなりグダグダでした・・。すいません。何せまだモノがありませんからね・・・。少なくとも今年中にプロトタイプは作りたいと目論んでいるんですが。


二次会はニュージーランドのお店。Canterbury Draughtがおいてたらいいなあ、なんて思いましたがさすがにおいてませんでした。皆さんお疲れさまでした。

Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - 方眼紙みたいなSwingコンポーネントを作る, その4

具体的なプロトタイプ設計に入る前に、別のアイデアを忘れてしまわないようにメモ書きしておきます。今回、当初の目標はHTMLのtableと同程度の表現能力を考えています。さて、表現能力の獲得はがんばればよいとして、その3で紹介したようにセルが結合されているような表をAPIで指定していくのは面倒くさそうです。たとえば、こんな感じになるんでしょうか。

Hogan h = new Hogan();

// (3, 2)の位置のセルでセル幅を2にして結合
h.setColumnSpan(3, 2, 2);

// (4, 6)の位置のセルでセル高を2にして結合
h.setRowSpan(4, 6, 2);

これはこれで必要なAPIですが、これだけで表を作り上げていくのはかなり面倒くさそうです。ですので、下記のようなイメージで、流行の流れるインタフェースというのを使ってはどうかと考えています。

// Builderを通してほぼHTMLの感覚でかける。
Hogan h = HoganBuilder.table()
  .thead().tr()
    .th("銘柄", Style.rowSpan(2))
    .th("始値")
    .th("高値")
    .th("前日比")
    .th("出来高", Style.rowSpan(2))
    .th("売気配")
  .tr()
    .th("終値")
    .th("安値")
    .th("前日比%")
    .th("買気配)
  .tbody().tr()
    .td("ABC", Style.rowSpan(2))
    .td(6460, Style.decimalFormat("#,##0"))
    .td(7360, Style.decimalFormat("#,##0"))
    .td(2000, Style.decimalFormat("+#,##0;-#,##0"))
    .td(1275, Style.decimalFormat("#,##0").rowSpan(2))
    .td("---", Style.decimalFormat("#,##0"))
  .tr()
    .td(7360, Style.decimalFormat("#,##0"))
    .td(6020, Style.decimalFormat("#,##0"))
    .td(0.373, Style.decimalFormat("+##0.0%;-##0.0%"))
    .td(7360, Style.decimalFormat("#,##0"));

// 行の追加
h.addRow(HoganBuilder.tr()
    .td("DEF", Style.rowSpan(2))
    .td(55000, Style.decimalFormat("#,##0"))
    .td(56000, Style.decimalFormat("#,##0"))
    .td(-1800, Style.decimalFormat("+#,##0;-#,##0"))
    .td(61899, Style.decimalFormat("#,##0").rowSpan(2))
    .td(52800, Style.decimalFormat("#,##0"))
  .tr()
    .td(52700, Style.decimalFormat("#,##0"))
    .td(52200, Style.decimalFormat("#,##0"))
    .td(-0.033, Style.decimalFormat("+##0.0%;-##0.0%"))
    .td(52700, Style.decimalFormat("#,##0")));

// 行の追加
h.addRow(HoganBuilder.tr()
    .td("GEF", Style.rowSpan(2))
    .td(32350, Style.decimalFormat("#,##0"))
    .td(32400, Style.decimalFormat("#,##0"))
    .td(300, Style.decimalFormat("+#,##0;-#,##0"))
    .td(144616, Style.decimalFormat("#,##0").rowSpan(2))
    .td("---", Style.decimalFormat("#,##0"))
  .tr()
    .td(30650, Style.decimalFormat("#,##0"))
    .td(30100, Style.decimalFormat("#,##0"))
    .td(0.009, Style.decimalFormat("+##0.0%;-##0.0%"))
    .td("---", Style.decimalFormat("#,##0")));

hogan.png
このようにかければ、わりと拡張の融通も利きそうですし、HTMLで表を作ったことがある人ならば、違和感なく使えると思います。スタイルの指定をどうするかはもう少しアイデアを練り込む必要がありそうです。なお、上記の例では毎回、それぞれの行で書式を指定していますが、このあたりは冗長に見えるので、テンプレート機能のようなものを用意しても良いだろうと思っています。

// 株価クラス
public class StockPrice {
  private String symbol;
  private int startPrice;
  private int endPrice;
  private int highPrice;
  private int lowPrice;
  private long volume;
  private int diff;
  private float diffPercent;
  private int ask;
  private int bid;
  public void setSymbol(String symbol) { this.symbol = symbol; }
  public String getSymbol() { return this.symbol; }
  ... // 以下 setter/getterやコンストラクタなど
}

// 株価情報
StockPrice s = new StockPrice("DEF", 55000, 52700
    , 56000, 52200
    , -1800, -0.033f
    , 61899
    , 52800, 52700);

Hogan h = ... /// ... ベースとなる表の作成

RowTemplate tmpl = RowTemplateBuilder.tr()
    .td("symbol", Style.rowSpan(2))
    .td("startPrice", Style.decimalFormat("#,##0"))
    .td("highPrice", Style.decimalFormat("#,##0"))
    .td("diff", Style.decimalFormat("+#,##0;-#,##0"))
    .td("volume", Style.decimalFormat("#,##0").rowSpan(2))
    .td("bid", Style.decimalFormat("#,##0"))
  .tr()
    .td("endPrice", Style.decimalFormat("#,##0"))
    .td("lowPrice", Style.decimalFormat("#,##0"))
    .td("diffPercent", Style.decimalFormat("+##0.0%;-##0.0%"))
    .td("ask", Style.decimalFormat("#,##0")));

// 株価情報JavaBeanからテンプレートに沿って行に展開
h.addRow(tmpl.newRow(s));

このようにたとえばRowTemplate・RowTemplateBuilderのようなクラスを作っておき、実際に付け加えるデータはStockPriceインスタンスのように管理しやすいままの形で複雑な形式の行を追加します。この例でのRowTemplateBuilderのtd(“symbol”, … のような第一引数はプロパティ名です(拡張してELに対応するか、BeansBindingに対応してもおもしろいですね)。ここではJavaBeanに対するアクセスを考えていますが当然、HashMap等にも応用できます。
この例は表示スタイルについてのみ定義していますが、ほかの発展イメージとしてはバリデーションルールの追加や、エディタやレンダラの指定なども考えられます。

Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - 方眼紙みたいなSwingコンポーネントを作る, その3

だいぶデザインコンセプトが固まってきました。最初のプロトタイプはいきなり実装を始めてプログラミングの感覚を取り戻すことにしようかと思っていましたが、それではもったいないのできちんとした設計からやる方針に変えました。きちんと設計を進めてみたところ、今回作ろうとしているのは単に表としてデータを羅列するための役割を持った部品とするよりも、ある情報アーキテクチャに基づいてデータをユーザに公開したり、ユーザからのアクションに対応するためのフレームワークと考えた方がより汎用的です。それに、それぐらいの設計上の敷居をあげておかないとリハビリとしては甘いような気もします。
_DSC3019
最初、どれぐらい汎用的なデータモデルを用意すれば、複雑な表にも対応できるか考えるために複雑な表の例を考えることにしました。
simpletable.png
まずJTableでも実現できるもっともシンプルなテーブルを考えます。列がいくつかあって、ヘッダがあります。データは一行ごとに表示される形式です。これならばデータの形式はJTableのTableModelのように行ごとに管理するのが良さそうです。セルの結合が多少あっても、それはレンダリングのためのヒント情報を与えてやれば解決できそうです。もう少し複雑な例を考えてみます。
multiple row in single data
セルやヘッダが結合された上に、ひとつのデータの固まりが複数行を使う場合です。ここでは例として株価情報にしていますが、一件のデータについて表示項目がかなり多くなる場合には、横にデータを広げるよりも折りたたんで、複数行で表現するほうが可読性が向上する場合があります。このような表を扱うような場合、表示上の行と、データ上の行は必ずしも一致しなくなります。JTableのTableModelでこのような表を扱うのはかなり難しくなります。
幅などに応じて伸縮する表
次の例は、たとえばテーブルの表示幅にあわせて列数が可変になるような表の構造です。この例ではデータは左上の端から右方向に詰め込まれ、表示幅が足りる範囲で右方向に成長します。表示幅が足りなくなれば、次の行に改行します。このような表はショッピングサイトなどのカタログでよく見受けられます。この表のデータモデルは、一次元的ではありますが、表示上の位置関係とは独立したデータモデルとなり、表示位置は動的に決定されます。
_DSC3017
さて、表の例としては上記以外にも様々なパターンが考えられますが、様々な形式の表に対応するためには、モデルが多様なデータ形式を受け入れるようにするか、複数パターンのデータモデルを受け入れるようにするなど、いかにも使い勝手が悪くなりそうなにおいのする方向で設計が進みそうです。ここでは、当初のネーミング通り、方眼紙というコンセプトに戻ることにしました。
方眼紙からいろんな表へ
まずは、もっともシンプルな方眼紙のようなコンポーネントを設計し、それをベースとして多様なデータ表現を実現できるような拡張の形で広げていく方法です。これならば扱うデータモデルの複雑さとAPIとしての使い勝手のバランスがうまくとれそうです。この設計が良いかどうかまだ分かりませんが、あまり設計ばっかり考えていてもリハビリにならないのでだいたいこの方針で、随時軌道修正しながら実装プロトタイプを作っていってみることにします。

Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - 方眼紙みたいなSwingコンポーネントを作る, その2

これから作るSwingコンポーネント、JHogan(仮)の設計方針についてまとめておきます。

  • UIクラスも自作する。レイテンシ上、シビアになった場合のチューニング余地を広げるため。また、デザイン上も独自の工夫をするのが楽しそうだから。
  • JScrollPane相当も自作する。フォーカスの制御や、複雑なスクロールの制御をするなら自作した方が簡単そう。
  • プラグイン可能なアーキテクチャ。いろいろ追加的機能はどんどん欲しくなると思うので、機能を動的につけたり外したりしやすい感じのアーキテクチャをねらいます。
  • アノテーションやジェネリクスの活用。使えるところでは積極的に使います。
  • ツールの作成。ある程度複雑な表を作ろうとすると、JHoganのAPIをたたいて表を設計するのは面倒くさくなりそうなので、表のテンプレートを作るためのツールも作ることにします。ただし、ツールばっかりに注力しないように、優先順位は低めに設定します。
Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - 方眼紙みたいなSwingコンポーネントを作る, その1

新たに設計を始める前に、もっとも類似で参考とすべきクラスJTableを使ってみての感想まとめておきます。今回はリハビリ目的と言うこともあって、JTableを拡張するよりは、JTableではなく全く新たにコンポーネントを設計しようと思っています。これは、JTableだけでなく、画面スクロールのためのJScollPaneも同様に新たにこの方眼紙コンポーネントのために設計することにします。では、既存のSwingコンポーネントJTableとJScrollPaneを使った場合の問題点を。

  • TableModelが抽象的すぎる。ある程度複雑な表を扱おうとすると、TableModelが定義しているインタフェースでは不足ぎみです。リッチな表現を実現するためには、JTableかTableModelのどちらかでレンダリングのためのコンテキスト情報(たとえばセル結合とか、背景色とか)を持つ必要がありますが、現状のJTable・TableModelのコンビネーションではこれらのデータを扱う方法が十分考慮されているようには思えません。TableModelを実装したクラスでそれらの情報を管理しても良いのですが、TableModel相当の実装が渡されたときの互換的な振る舞いを定義するのが面倒なことと、より適切にテーブルモデルを設計することもリハビリの一環と考え車輪の再発明をすることとします。
  • ヘッダとなるべきセルが上部以外にある場合、スクロールとフォーカスの扱いがJTableとJScrollPaneの組み合わせではややこしい。たとえば、表の左側を固定してヘッダとしたい場合、典型的な実装例ではJScrollPaneのRow Headerに別のJTableインスタンスや、JListインスタンスを設定して、データ内容や表示を同期したりします。単純な例ではそれでも良いのですが、フォーカスの移動や、スクロール、データ内容更新に伴う列サイズの更新などが生じた場合のハンドリングが複雑で、バグが生じやすく、また、再現が難しいケースもあるので実装としては避けたい方式です。
  • JTableを拡張する場合、Javaバージョン追随に対して十分ケアしなければならなくなる。Java SE 6ではJTableに対してsortやDropModeなどの拡張が追加されていますが、Java SEのバージョンアップに伴って一般的なメソッド名として追加されてしまうと、のちのち重複した名前をつけてしまった場合に面倒になります。特に、一般的な名称は標準的な仕様でりようされてしまい、回避的な名称にすると、理解しづらくなってしまうので新たにクラスを起こす方が適切だろうと考えています。
  • 複雑なヘッダ構造に対応できない。ヘッダとなる行(あるいは列)が複数行で構成されるような表に対応することは現状のJTableHeaderでは難しい。
  • プラグイン的に振る舞いを追加することがあまり簡単ではない。表のようなコンポーネントには様々な要件が日常的に要求されますが、JTableは処理の多くを隠蔽しているので、多様な処理をあとから付け足すためにはややこしい処理を余儀なくされたり、せっかく作ってもJavaのバージョンアップとともに挙動が変わってしまう可能性もあります。

JTable継承しないデメリットもあります。JTableを含め標準的なSwingコンポーネントはLook & Feelに応じたUIレンダリングクラスが標準的に用意されています。その一方で、新たにコンポーネントを起こす場合には、各Look & Feel向けにそれらのUIレンダリングクラスを実装する必要が生じます。このことがSwingでカスタムコンポーネントが初期に想定されたほど開発されなかった原因の一つだと思います。ただ、これは今回、マルチLook & Feel対応を初期段階ではあきらめることで許容範囲内とします。余裕があれば、複数のLook & Feelに対応しても良いのですが面倒なのできっとしないと思います ;-P

Share on Facebook
Share on GREE
このエントリーをはてなブックマークに追加
はてなブックマーク - プログラミングのリハビリ。方眼紙みたいなSwingコンポーネントを作りはじめる。

最近、サンプルプログラムを動かしたり、ちょっとしたシェルスクリプトを書く以外ではめっきりプログラミングをしなくなったので、リハビリをしようと思います。リハビリ目的なので、ある程度複雑な題材にチャレンジすることにします。ちょうど、前から作ろうと思っていた題材があるのでそれを作ることにします。今回作ろうと思うのは、JavaのSwing向けに方眼紙みたいなコンポーネントです。
JTable
SwingにはJTableというコンポーネントがありますが、ある程度複雑なことをしようとすると、とたんに破綻します。たとえば、それぞれの行にヘッダ(左端の列をヘッダとする)を付与したり、セルを結合したり、同一列内で複数のデータ型を扱うような場合です。これらの問題を解決するような新たなコンポーネントを作ることは、リハビリの題材として丁度規模的にも、難易度的にも丁度良さそうです。このような表のようなコンポーネントは、特に日本市場では成熟されたExcelによる業務システムが暗黙的な比較対象となることもあって機能的にも際限なくテーマが与えられますし、ユーザインタフェースとしては十分機敏に動かなければならないのでパフォーマンスチューニングや、ある種トリッキーなアルゴリズムを思い出すきっかけになりそうです。ひとまず、2〜3回ほど使い捨てのプロトタイプを作って実証実験をした後に、そこそこ形となるモノを仕上げてみたいと思います。