table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
のようにする。
- ListSelectionModel.SINGLE_SELECTION
- ListSelectionModel.SINGLE_INTERVAL_SELECTION
- ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
が指定可能。
どーしてこうもモデルが大量に詰め込まれているのか>JTable。
ある意味で機能的かもしれないが、分かりにくく使いにくくバグりやすい原因のひとつは、この複合モデル構造だと思うぞ。
機能構造単位できちんといぢるべきところを把握できていれば、必要なモデルだけをいじるからバグは出にくいしテストもしやすいだろう。
でも、きちんと把握しきれない(する余力が無い)状況下でいぢると、まず間違いなくバグを産むはず。いぢるところが間違っているかもしれないし、いぢりかたも間違っているかもしれないし。
こいつは、チーム内で使うDBアクセスライブラリを組んでいるあたし自身も、常に注意・反省をしないといけない点だけど…。
でも分からなかったら早めに聞いてほしい…。
プロジェクトの立ち上げから参加し、顧客とあるべき仕様について議論し、色々と乗り越えて完成したシステムが、「あれは実情に合わない」「これは性能不足」と次々に駄目出しをされ、徐々に死んでいく様を見送るのは果たして幸か不幸か。
人間に例えるなら、つきあうきっかけから始めて出産の瞬間に立会い、「将来きっとXXするに違いない!」と期待されるのを聞いた上で、その後周囲から「あいつはXXが駄目だった」「もうあいつには何も期待しない」といわれながら醜く老衰していくのを見届け、終いには墓まで見送るよーなものだと思うが。
まぁ、あたしたちの仕事ってのは、一発で完璧な永久機関つくっちゃうとその後路頭に迷うことになるから、一定期間後には陳腐化して捨てられるものの方が長期的にはいいことなんだろうけどさ。
感情的には見ないで立ち去るほうが幸せかねぇ。中にはあの世まで立ち去っちゃった人もいるけど…。
職場で後輩に教えたことの備忘録だけど、後々役立つかもしれないからこっちにも保存。
権利云々は…大丈夫だと思うけど、この程度なら。書いたのあたしだし、文面考えたのもあたしだし。特定業務のネタも…入ってないな、よし。
なお…。あたし個人の方針として定義したパターンなので、明らかな間違い以外は突っ込み無用デス。
1. 基礎知識
1.1 Swingについて
- Swingに関わる処理はスレッドセーフではない。
- Swingは、Swingに関わる処理を行うためのスレッドを1本だけ持っている。
- Swingスレッド以外のスレッドからSwingに関わる処理を実行するためには、Swingスレッドに処理内容を伝達する必要がある。
- Swingスレッド以外のスレッドからは、Swingスレッド内でどの処理を実行しているか(ある処理が終わったか否か)を知ることはできない。
- Swingスレッドは1本しかないので、スレッド内で長時間処理を行うと、その間GUIは更新されずにフリーズし、白抜き画面などになってしまう。
1.2 スレッド、Runnableについて
- Runnableは「run()メソッドを持つ」ことを定義するインタフェース。
- スレッドはRunnableインタフェース実装クラスの1種。
「run()メソッドに書かれた処理を、他のスレッドとは独立した時間軸で実行する」
- スレッドの開始は、start()メソッドで行う。start()の実行によってスレッドはJava VMに対し、実行権を要求する。
直接run()を実行しても、スレッドにはならない。
- Java VMが実行権を与えたタイミングで、スレッドのrun()が実際に実行される。
2. Swing GUI処理の実装基本パターン
2.1 お約束
- Swingに関わらない処理は、基本的にSwingから独立したスレッドとして実装する。
2.2 基本パターン
- -- Swingイベントスレッド内 --
- Swingイベントの発生(ボタンのActionPerformed()呼び出しなど)。
- 処理に必要なデータをGUIから取得(選択されているデータ、入力文字列等)。
- 処理スレッドのインスタンス生成、必要データのセット。
- GUIのロック(全ボタンを使用不能にする、等)。
- 処理スレッドの開始、Swingイベントの終了。
- -- 以下、処理スレッド内 --
- 処理の実行(DBアクセス、計算等)。
- GUI更新Runnableのインスタンス生成、GUI更新データのセット。
- GUI更新RunnableインスタンスをSwingUtilities.invokeLater()を使用してSwingイベント待ち行列に追加。
- ループ処理の場合、8)~10)を繰り返す。
- (全処理終了後)GUIのロック解除Runnableのインスタンス生成、
SwingUtilities.invokeLater()を使用してSwingイベント待ち行列に追加。
- 処理スレッドの終了。
- -- 以下、再びSwingイベントスレッド --
- 10)で追加されたRunnableを順番に実行。
- 12)で追加されたRunnableを実行、GUIロック解除。
- 全終了。
JTableHeader header = table.getTableHeader();
header.setFont( new Font( "Dialog", Font.BOLD, 24 ) );
みたいな感じで。他にもセルレンダラとか使う方法があるみたい(むしろこっちが正道?)だけど、どーやってもうまくいかなかったからコレでいいだろ。
「SwingでExcelを作る(マクロ、計算機能除く)」ってのは、立派な検定試験とかになりそうな気がする…。ある意味でJavaMailの日本語処理より面倒だ。
概ね自分幼のメモれす。
- テーブル内の幅を変更したときに横スクロールするようにするには、JTable#autoResizeModelにAUTO_RESIZE_OFFをセットする。出ないと表示範囲にあわせてセルを縮めてしまう。
- …カラムのminSizeをセットしてもイけるか?
- セルのレンダリングはTableModel#getColumnClass()できちんとセルの型を示さなきゃダメよ。
- セルの編集可否は、TableModel#isCellEditable()で判定。AbstractTableModelの初期値は全false。
JTableは何度いじっても忘れる。複雑すぎるのよねココ。
概ね自分幼のメモれす。
- IMAP4アクセスでのお話。POP3は未確認。
- Store#getDefaultFolder()で取得できるFolderは、#open()できない。
まぁAPIにも「This method is valid only on Folders that
can contain Messages~」って書いてはあるんだけど。
…「DefaultFolderか否か」ではなくて、「#getMessageCount()が0でないか」で判定すればいいのか?もしかして。
- 実は、DefaultFolderでなくても空っぽのFolderは#open()できなかったりして…。
- Folderの中身は、#list()でFolder[]を取る分には、#open()しなくてOK。なので、中身としてFolderしか持ち得ないDefaultFolderについては、#open()出来なくても別に害はない。
- Folderから#getMessages()とかでMessageを取るときには、#open()しないといけない。
ん~。なんかさー。「フォルダ名を指定したらその名前でStore#getFolder(String)、null指定ならStore#getDefaultFolder()」ってな仕様のラッパーを作りたかったんだけど…。
余計にめんどくなったわ。