ugnagブログ

たいした内容はありません。思いつきで書いているだけ。
開発日記がメインかな。

<< JTableのセルカーソルの位置 | main | JTableのセルカーソルの位置(その3) >>

JTableのセルカーソルの位置(その2)

前回の続き。
JTableのセルカーソルの位置の取得だが、

一応、いろいろと試してみた結果、なんとかなったのではないかと思うので、報告してみる。




基本的には、JTable#changeSelectionメソッドをオーバーライドするしかないようだ。

個人的な方針として、JTableをオーバーライドしたくはないのだが、
他に方法が無さそうなのでしかたない。


このメソッドは、JTableの行又は列の選択に変化があるときには、
必ず呼び出される。

そのため、このメソッドをオーバーライドし、もともとの処理の終了後にイベントという形で、通知するのがいいかと思う。

イベントとなると、インターフェースやらイベントオブジェクトやらを作らなければならないが、

今回は、既に実装されているPropertyChangeListenerを使ってしまうことにした。


つまり、JTableのセルカーソルの位置が変わった際には、

PropertyChangeListenerに対しイベントを発生させる。
その際のプロパティ名は、"cell_cursor_position_changed"とでもしよう。



PropertyChangeListenerならリスナーの追加や削除、イベントの発火などの機能は実装されているので楽だ。


また、どうせJTableをカスタマイズするなら、セルカーソルの位置を取得するメソッドも実装してしまった方が便利だ。


public JTableEx extends JTable
{
    static public String Prop_CellCursorChanged = "cell_cursor_position_changed";

    @override
    public Point getCellCursorPosition(){

        int r = getSelectionModel().getLeadSelectionIndex();
        int c = getTableHeader().getColumnModel().getSelectionModel().getLeadSelectionIndex();


        return new Point(c, r);

    }


    @Override
    public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {

        super.changeSelection(rowIndex, columnIndex, toggle, extend);

        fireCellCursorChanged();

    }



    /**
     * リスナーに現在のセルカーソルの位置を通知する
     */
    protected void fireCellCursorChanged(){

        Point pt = getCellCursorPos();
        firePropertyChange(Prop_CellCursorChanged, null, pt);

    }

}




さて、これで試してみると、問題点が4つあることが発覚した。


1.マウスのドラッグで範囲を選択しているときに、セルカーソルの位置に変更がないのに、マウスカーソルの位置が変わっただけで、イベントが発生してしまう。


2.ctrl + カーソルキーでの操作をすると、選択範囲が変更されること無く、セルカーソルの位置だけが移動する。
選択範囲が変わらないので、changeSelectionが呼ばれず、PropertyChangeイベントが発生しない。


3.範囲が選択されている状態で、エンターキーやTABキーを押すと、範囲が変更されること無く、セルカーソルの位置が、範囲内で移動する。
これもやはりPropertyChangeイベントが発生しない。


4.セルカーソルが選択範囲の外にある時に、エンターキー又はTABキーを押すと、選択範囲は変更されること無く、セルカーソルが選択範囲内に移動する。
これもやはりPropertyChangeイベントが発生しない。

この場合の「選択範囲」には、複数選択ではないものも含む。



この内、困るのはエンターキー/TABキーだ。
これらは、選択範囲が単数の行/列の状態で押されると、セルカーソルは下あるいは右へ移動するが、その際に選択範囲も一緒に移動する。

このとき、changeSelectionが呼び出され、ropertyChangeイベントが発生してしまう。

つまり、エンターキー/TABキーの処理をうまく行わないと、PropertyChangeイベントが2回発生してしまう。



以上のことを注意しながら、解決案を考えてみた。


1.セルカーソルの位置に変化があった場合のみ、イベントを発生できないか。

調べてみたところ、changeSelectionメソッドの処理前ではセルカーソルの位置は動いていない。

それなら話は簡単だ。

以下のように修正する。


@Override
public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {

Point cursorPosOld = getCellCursorPos();

super.changeSelection(rowIndex, columnIndex, toggle, extend);

Point cursorPosNew = getCellCursorPos();



if(!cursorPosNew.equals(cursorPosOld)) {
fireCellCursorChanged(cursorPosOld);
}

}


/**
* リスナーに現在のセルカーソルの位置を通知する
*/
protected void fireCellCursorChanged(Point posOld){

Point pt = getCellCursorPos();
firePropertyChange(Prop_CellCursorChanged, posOld, pt);

}




ついでに、変更前の位置を一緒に通知出来るように、fireCellCursorChangedも修正。


これで、1の問題はクリアした。




2.の問題だが、これはctrl+カーソルキーの処理を変更してイベントを発生させるしかないと思う。

キー入力に対する処理の変更だが、InputMapやActionMapを操作して変更することになる。


InputMapやActionMapについて簡単に復習しておくと、

Swingではキーボード入力に対する処理は、InputMapとActionMapというものを使って実装することが出来るようになっている。


それぞれを簡単に説明すると、

InputMap

入力されたキー入力から、コマンド名を取得できる。


ActionMap

コマンド名から、実際の処理を取得できる。





例えばエンターキーの場合は、

InputMapで、
エンターキー → "selectNextRowCell"

ActionMapで、

"selectNextRowCell" → (実際の処理)


となる。

「(実際の処理)」というのは、Actionオブジェクトで、
処理そのものは、actionPerformedメソッドになる。



で、これを変更するには2つ方法がある。


A.新しくActionMapに、コマンド名/処理を追加し、
InputMapには新しいコマンド名を割り当てる。


B.InputMapには変更を加えず、ActionMapの当該コマンド名に対する処理(Actionオブジェクト)のみを入れ替える。



Aが正当な気もするが、エンターキーとTABキーの動作を入れ替えるということを、汎用的に行ないたい予定もあり、ここはBの案で行くことにした。



長くなったので、続きは次回へ。
JAVA | comments (0) | -

Comments

Comment Form

本文に書いて下さい
本文にh抜きで書いて下さい