2011/08/09

Java で、てきとう Twitter クライアント

前回、ポータブル版の Eclipse を導入したので、調子に乗って超手抜き Twitter クライアント『たーちんII号』を作ってみる事にしました。


なんでこんな事をしたかというと、表示される Twitter クライアント名にオリジナリティを出したかったからです(えっへん)。

うまくいかない…(´;ω;`)ウッ…2011年8月9日 20:54 via たーちんII号



【幻の1号】

実は、僕が Twitter クライアントを作るのは初めてではありません。

去年の9月15日くらいに、1st クライアント『たーちん』をそれはそれは適当に作ったのでした(出来上がりはそれはもう酷いものでした)。

テスト∩( ・ω・)∩ テストテスト2010年9月15日 19:45 via たーちん


@tercel_s ついに…作ったのですね!!2010年9月15日 19:49 via SimplyTweet

当時は NetBeans という統合開発環境に慣れるための試運転的な意味合いが強かったのですが、やはり制作物の名前が Tweet に載るのはこそばゆいものです。

しかし今回、制作にあたって、Twitter の OAuth 認証で必要な Consumer KeyConsumer Secret は初代(1号)のものをそのまま流用したため、事実上のリネーム扱いになってしまい、もはや Web 上で初代たーちんの名前を確認する事はできなくなってしまいました。

とはいえ1年前よりもほんのちょっとですが腕が上がりましたので、より効率的な実装のためにソースコードは全て書き直しました。さらに、今をときめく Streaming API も使ってみました∩( ・ω・)∩



【つまづきポイント】

今回は Java 向けに Twitter API をラップした Twitter4J というライブラリを使用。使い方に関しては、『陽昇れども地の底に光届かず』 というサイトの解説が非常に参考になりました。

作っていてつまづいたポイントの中でも特に厄介だったのが表示関連でした。これは Twitter API の知識というよりも、GUI プログラミング(Java の場合は Swing)のスキルが重要になります。

今回制作したクライアントでは、各ツイートの表示に JTable を使用しています。 1行あたり1ツイート。各カラムは、Twitter のユーザ名やツイート内容などの項目に対応するわけです。

かつての JTable は非常に使い勝手が悪いものでしたが、Java SE 6 以降では、任意のカラムをキーにして表をソートする事が可能になり、テーブルの閲覧性・操作性が劇的に向上しました。

たとえば各ツイート(行)を時系列でソートすれば、テーブルはタイムラインとして機能する事になりますし、ユーザ名でソートすれば特定のユーザを重点的に閲覧したい場合に大変便利です。

JTable は、データの実体であるモデルと、『表』として視覚化されたビューとに分離する事ができるようになっています。いわゆる MVC に近い機構で、内容が動的に変化するような表を柔軟に扱える大きな利点を有しています。

ですが。

ここで新しく受信したツイートをソート済みのテーブルに次々と追加していく場合を考えましょう。

テーブルに行を追加すると、TableRowSorter によって自動的に(というか勝手に)ソートされます。

そして、行の追加によってテーブルが表示領域に収まりきらなくなった場合には、JScrollPane によってスクロールバーが表示されます。

このとき、JScrollPane 内の可視領域(JViewPort)が必ずしも意図した結果にならないという問題点があります。

例えば、100行を一度に追加した事によって、それまで表示されていたテーブルの領域が押し出されてしまえば、ユーザはたちどころに所望のデータの位置を見失うでしょう。

つまり、データの追加前・追加後で、できるだけ同じビューをユーザに対して見せる工夫が必要になってくるのです。

以下の図の左側は、ある時点でのスクリーンショット(一部)を示したものです。 そして、右側は、それから一定時間経過後のスクリーンショットです。データが追加されたため、スクロールバーの位置は変わっていますが、見えているビューはどちらも同じです。


このような処理を実装するのはそれほど難しくはありません。ただし、条件として、テーブルの行の高さが一律でなければならないという条件があります。

// 前提知識コーナー: GUI はこうやって構築された!!
// 
// DefaultTableModel tableModel 
//                         = new DefaultTableModel(null, new String[] { /* 中略 */ });
// JTable table            = new JTable(tableModel);
// JScrollPane scroll      = new JScrollPane(table);
// JViewport tableViewport = scroll.getViewport();

private void addToTable(RowData data) {
    // ビューの位置計算
    Point pt = tableViewport.getViewPosition();
    int pOffset = table.getRowHeight() * table.getSelectedRow() - pt.y;

    // テーブルにデータを追加(ここは適当にいじってね)
    tableModel.addRow(data);

    // 追加後の位置あわせ
    int offset = table.getRowHeight() * table.getSelectedRow();
    int newY = Math.max(0, offset - pOffset);
    tableViewport.setViewPosition(new Point(0, newY));
}

これで、クライアントを起動したまま席を離れても、TL を見失う事がなくなりました(たぶん)。めでたしめでたし。



他にもいろいろ躓いたポイントはありましたが、Twitter4J のライブラリとしての使い勝手が割とよかったため、本質的な処理の実装に関して悩む時間は相対的に少なかった気がします。

特に、User Stream に関して言えば、何かしら新しいツイートを受信すると、事前に登録しておいたイベントリスナがコールバックされるスマートな仕様になっているおかげで、従来の REST API よりも扱いがラクだと感じました。

一方で、バックエンドの処理結果を Swing に紐付けるスマートな方法論を模索する泥臭いトライ & エラーも楽しいです。今回、ようやく匿名クラス(anonymous class)の使いどころがなんとなく解ってきましたし、マルチスレッドの環境下でデータの重複管理を行うためのパターンも身につきました。



【おまけ】

うれし恥ずかし、1年前に作った初代たーちんのスクリーンショット。
……あれ?

やっぱり昔の方がセンスあったんじゃないかな?

0 件のコメント:

コメントを投稿

ひとことどうぞφ(・ω・,,)