4

JTree の各エントリに対応するオプションが別のパネルにあり、選択が変更されると更新されるダイアログがあります。エントリの 1 つのオプションが無効な状態に設定されている場合、ユーザーがツリー内の別のエントリに変更しようとすると、エラー ダイアログが表示され、選択が変更されないようにする必要があります。

JTree で valueChangeListener を使用してこれを実行しようとしましたが、現在、エラーが発生した場合、valueChanged メソッドで古い選択に対して「setSelectionRow」を呼び出す必要があります。StackOverflow を取得しないように、これを行う前にブール値の「isError」を true に設定して、新しい valueChanged イベントを無視できるようにします。どういうわけか、これは最善の解決策ではないと直感しています。;-)

代わりにどうすればいいですか?このような状況に適した設計パターンはありますか?

4

7 に答える 7

7

より良い方法は見つかりませんでしたが、このアプローチは私にとってはうまくいきます。Delphi では、「選択を変更する前」に選択の変更を非常に簡単に停止できる、非常に便利なイベントであったことを私は知っています。

これが無限再帰の問題を防止する私のJavaコードです

    navTree.addTreeSelectionListener(new TreeSelectionListener() {

        boolean treeSelectionListenerEnabled = true;

        public void valueChanged(TreeSelectionEvent e) {
            if (treeSelectionListenerEnabled) {
                if (ok to change selection...) {
                    ...
                } else {
                    TreePath treePath = e.getOldLeadSelectionPath();
                    treeSelectionListenerEnabled = false;
                    try {
                        // prevent from leaving the last visited node
                        navTree.setSelectionPath(treePath);
                    } finally {
                        treeSelectionListenerEnabled = true;
                    }
                }
            }
        }
    });

メモリリークを防ぐために、追加したすべてのリスナーを必ず削除してください。

ここに別のアプローチがあります:

private class VetoableTreeSelectionModel extends DefaultTreeSelectionModel {
    public void setSelectionPath(TreePath path){
        if (allow selection change?) {
            super.setSelectionPath(path);
        }
    }
}
{
    navTree.setSelectionModel(new VetoableTreeSelectionModel());
}
于 2009-02-22T01:19:31.003 に答える
3

ベストプラクティスかどうかはわかりませんが、検証するコンポーネントにFocusListenerを配置することもできます...イベントが呼び出されたときに検証を呼び出し、フォーカスを移動したくない場合はイベントを消費します検証が失敗したためですか?

後で編集:

少なくともjava8(以前のバージョンはチェックしていません)では、FocusEventは低レベルのイベントではないように見えるため、このソリューションは機能しません。したがって、それを消費することはできません。メソッドAWTEvent.consume()を参照してください

于 2008-11-04T10:42:55.603 に答える
3

これが私の解決策です。

JTree サブクラス:

protected void processMouseEvent(MouseEvent e) {
        TreePath selPath = getPathForLocation(e.getX(), e.getY());
        try {
            fireVetoableChange(LEAD_SELECTION_PATH_PROPERTY, getLeadSelectionPath(), selPath);
        }
        catch (PropertyVetoException ex) {
            // OK, we do not want change to happen
            return;
        }

        super.processMouseEvent(e);
}

次に、クラスを使用してツリーで:

VetoableChangeListener vcl = new VetoableChangeListener() {

        public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
            if ( evt.getPropertyName().equals(JTree.LEAD_SELECTION_PATH_PROPERTY) ) {
                try {
                    <some code logic that has to be satisfied>
                } catch (InvalidInputException e) {
                    throw new PropertyVetoException("", evt);
                }

            }
        }
    };
    tree.addVetoableChangeListener(vcl);

メカニズムは可能な限り早い場所から開始されます。マウス操作がインターセプトされ、選択されるパスが VetoableChangeListeners にアドバタイズされます。具体的な VCL では、変化するプロパティが調べられ、リード選択の場合は拒否ロジックがチェックされます。拒否が必要な場合、VCL は PropertyVetoException をスローします。それ以外の場合、マウス イベントの処理は通常どおり行われ、選択が行われます。つまり、これにより、リード選択プロパティが制約付きプロパティになります。

于 2010-07-01T11:14:45.463 に答える
0

選択を防ぐために、DefaultTreeSelectionModel をサブクラス化し、すべてのメソッドをオーバーライドして、選択したくないオブジェクト (以下の例では "DisplayRepoOwner" のインスタンス) をチェックしました。オブジェクトが選択されても問題がなければ、スーパー メソッドを呼び出しました。そうでなければ私はしませんでした。JTree の選択モデルをそのサブクラスのインスタンスに設定しました。

public class MainTreeSelectionModel extends DefaultTreeSelectionModel {
public void addSelectionPath(TreePath path) {
    if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
        return;
    }
    super.addSelectionPath(path);
}
public void addSelectionPaths(TreePath[] paths) {
    for (TreePath tp : paths) {
        if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
            return;
        }
    }
    super.addSelectionPaths(paths);
}
public void setSelectionPath(TreePath path) {
    if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
        return;
    }
    super.setSelectionPath(path);
}
public void setSelectionPaths(TreePath[] paths) {
    for (TreePath tp : paths) {
        if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
            return;
        }
    }
    super.setSelectionPaths(paths);
}

}

于 2012-01-07T15:08:48.007 に答える
0

適切なセマンティクスを実装する TreeSelectionModel を設定します。

于 2008-11-05T16:49:38.593 に答える
-1

同じ問題の解決策を調査しているときに、このスレッドに出くわしました。まず、うまくいかなかった点を教えてください。MouseListeners とそのすべてをツリーに登録しようとしました。問題は、TreeUI のマウス リスナが、JTree より前にイベントを処理していたことです。つまり、フラグなどを設定するには遅すぎました。それに加えて、このソリューションはいくつかの醜いコードを生成し、私は一般的にそれを避けます.

それでは、実際の解決策をご紹介します。
いくつかの Thread.dumpStack() 呼び出しを使用してスタック ダンプを取得した後、オーバーライドしようとしていたメソッドを見つけました。BasicTreeUI を拡張し、「保護された void selectPathForEvent(TreePath パス、MouseEvent イベント)」をオーバーライドしました。

これにより、実際に選択が行われる前に、選択の原因となったマウス イベントにアクセスできます。次に、選択を停止する場合は event.consume() に必要なロジックを使用して戻り、必要な選択を行うか、super.selectPathForEvent(path, event); を呼び出してデフォルトの処理に渡すことができます。

JTree で作成した UI を設定することを忘れないでください。あのミスは私の人生の数分を無駄にしました ;-)

于 2010-09-10T23:14:52.287 に答える