NetBeans プロパティーエディタチュートリアル
このチュートリアルでは、カスタムエディタやカスタムインプレイスエディタ (custom inplace editor) の提供など、 NetBeans でプロパティーエディタを使う方法について紹介します。特に以下の内容を扱います:
- ノードごとに独自のプロパティーエディタを提供する
- カスタムエディタの作成
- カスタムインプレイスエディタの作成
- カスタムプロパティーエディタをグローバル登録する
はじめに
このチュートリアルは以下のチュートリアルの続きとして用意されており、サンプルコードはこれらのチュートリアルで作成したコードを基にしています:- NetBeans セレクション管理のチュートリアル I—TopComponent の Lookup を使う方法
- NetBeans セレクション管理のチュートリアル II—ノードを使う方法
- ノード API チュートリアル
もしもこれらのチュートリアルがまだお済みでないなら、先に済ませることをお勧めします。
サンプルをダウンロードするにはここをクリックしてください。
前回のチュートリアルでは、プロパティーシートに表示することができる Property を持つノードの作成方法を学びました。
カスタムプロパティーエディタ
標準のプロパティーエディタでは十分ではなかったり、標準のエディタが用意されていない型のプロパティーが必要になる時があるでしょう。NetBeans IDE には一般的な Swing の型に対するクラスが組み込まれていますが、標準のプロパティーエディタですべての需要がカバーできるというわけではありません。
これより前回のチュートリアルで中断したところ、つまり、 APIObject オブジェクトをラップする MyNode クラスが、 index という読み取り専用のプロパティーと、読み書き可能な date プロパティーを提供しているところから開始します。
プロパティーエディタの作成
プロパティーエディタ作成の基本はとてもシンプルです。JavaBeans API には、プロパティーエディタの基本をほとんどカバーする PropertyEditorSupport という基本クラスがあり、ほんの少しの手間で簡単なプロパティーエディタを作成することができます。
プロパティーエディタの役目は2つあります。プロパティーシートに表示する文字列から値にもしくは値から文字列へと変換することと、設定された値を検査することです。まず始めに、異なるフォーマットの日時を表示、入力できるプロパティーエディタを作成します。
org.myorg.myeditorパッケージを右クリックし、「新規」>「Java クラス」を選択します。ウィザードで、クラス名をDatePropertyEditorとします。- コードエディタで、クラスのシグネチャを変更し、
PropertyEditorSupportを拡張するようにします:public class DatePropertyEditor extends PropertyEditorSupport { setAsText()とgetAsText()を以下のように実装します:public String getAsText() { Date d = (Date) getValue(); if (d == null) { return "No Date Set"; } return new SimpleDateFormat("MM/dd/yy HH:mm:ss").format(d); } public void setAsText(String s) { try { setValue (new SimpleDateFormat("MM/dd/yy HH:mm:ss").parse(s)); } catch (ParseException pe) { IllegalArgumentException iae = new IllegalArgumentException ("Could not parse date"); throw iae; } }MyNodeをコードエディタで開きます。datePropertyの宣言部分を変更し、Propertyではなく、PropertySupport.Reflectionの変数として宣言します。そして、PropertySupport.Reflection独自の関数を使っていきます:PropertySupport.Reflection dateProp = new PropertySupport.Reflection(obj, Date.class, "date");- この行の後に、以下の1行を挿入してください:
dateProp.setPropertyEditorClass(DatePropertyEditor.class); - モジュールスイートを実行し、「ファイル」>「Open Editor」をクリックしてエディタを開き、以下のように Date プロパティーのフォーマットが変わることを確認してください:
カスタムエディタの作成
標準の java.beans.PropertyEditor とそのサブクラスのもう1つの特徴は、"カスタムエディタ" を作成することができるということです。これは通常、プロパティーシートの端にある "..." ボタンをクリックすると出てくるダイアログのことです。
このようなエディタの実装の詳細について説明することは、チュートリアルの本題からはそれるのですが、基本的には以下の通りです:
DatePropertyEditorの以下の2つのメソッドを実装します:public Component getCustomEditor() { return new JLabel ("I want to be a custom editor"); } public boolean supportsCustomEditor() { return true; }- スイートを実行すると、今度は以下のように、"..." ボタンがプロパティーシートの端に表示されます:
このボタンをクリックすると、 getCustomEditor() メソッドで作成した JLabel が表示されます:
実際に使用するには、JPanel を作成し、プロパティーを簡単に設定できるようにカレンダーや時計を追加します。話がそれるので、ここで実際のコードは紹介しません。
- 続きへ進む前に、以上で追加した2つのメソッドを削除します。
カスタムインプレイスエディタの作成
本当に便利な方法は、よりよい日時エディタをプロパティーシート自身に組み込むことです。NetBeans にはこのようなことをするための API があるのです。多少のコードは必要になりますが、そうするだけの価値はあります。
java.net の SwingLabs プロジェクトに、 date picker component という便利なコンポーネントがあるので、これを単純にリユースすることにします。まず最初にしなくてはいけないことは、NetBeans に SwingX を組み込むことです。
- SwingLabs のサイトから、
swingx.jarをダウンロードします。(ライセンスの都合により、 NetBeans の CVS で管理することはできません。) - SelectionSuite を展開し、「モジュール」ノードを右クリックして、「新規ライブラリを追加」を選択します:
- 先ほどダウンロードした
swingx.jarを参照します。「次へ」をクリックします。 - もう一度「次へ」をクリックし、コード名ベースが org.jdesktop.swingx となっていることを確認して「完了」をクリックします。
- プロジェクトタブで、My Editor プロジェクトを右クリックし、「プロパティー」を選択します。
- 「ライブラリ」カテゴリの「依存関係を追加」をクリックし、 swingx のライブラリラッパーモジュールへの依存関係を追加します。
これで date picker を利用する準備が整いました。これより、NetBeans 固有のインタフェースをいくつか実装します:
- ExPropertyEditor—プロパティーシートが環境変数 (
PropertyEnv) を渡すことができるプロパティーエディタインタフェース。この環境変数により、エディタは編集中のPropertyオブジェクトなどにアクセスすることができる。 - InplaceEditor.Factory—
InplaceEditorを所有するオブジェクトのインタフェース - InplaceEditor—プロパティーシート内にカスタムコンポーネントを表示するためのインタフェース
InplaceEditor.Factory と ExPropertyEditor を DatePropertyEditor に直接実装し、 InplaceEditor をネストクラスとして作成します:
DatePropertyEditorのシグネチャを以下のように変更します:public class DatePropertyEditor extends PropertyEditorSupport implements ExPropertyEditor, InplaceEditor.Factory {- これまでにもしたように、Ctrl-Shift-I キーを押してインポートを修正し、「すべての抽象メソッドの実装」を実行して必要な関数を追加します。
DatePropertyEditorに以下のメソッドを追加します:public void attachEnv(PropertyEnv env) { env.registerInplaceEditorFactory(this); } private InplaceEditor ed = null; public InplaceEditor getInplaceEditor() { if (ed == null) { ed = new Inplace(); } return ed; }- 次に
InplaceEditorを実装する必要があります。このクラスに、 swingx のJXDatePickerコンポーネントと、このコンポーネントに値を設定し、必要がなくなった際にリソースを解放する関数を追加します。多少のコードは必要になりますが、とても簡単です。DatePropertyEditorにInplaceという static なネストクラスを作成します:private static class Inplace implements InplaceEditor { private final JXDatePicker picker = new JXDatePicker(); private PropertyEditor editor = null; public void connect(PropertyEditor propertyEditor, PropertyEnv env) { editor = propertyEditor; reset(); } public JComponent getComponent() { return picker; } public void clear() { //avoid memory leaks: editor = null; model = null; } public Object getValue() { return picker.getDate(); } public void setValue(Object object) { picker.setDate ((Date) object); } public boolean supportsTextEntry() { return true; } public void reset() { Date d = (Date) editor.getValue(); if (d != null) { picker.setDate(d); } } public KeyStroke[] getKeyStrokes() { return new KeyStroke[0]; } public PropertyEditor getPropertyEditor() { return editor; } public PropertyModel getPropertyModel() { return model; } private PropertyModel model; public void setPropertyModel(PropertyModel propertyModel) { this.model = propertyModel; } public boolean isKnownComponent(Component component) { return component == picker || picker.isAncestorOf(component); } public void addActionListener(ActionListener actionListener) { //do nothing - not needed for this component } public void removeActionListener(ActionListener actionListener) { //do nothing - not needed for this component } } - まだインポートを修正していなければ、Ctrl-Shift-I キーを押してインポートを修正します。
- モジュールスイートを実行し、「ファイル」>「Open Editor」をクリックしてエディタ (もはや大したエディタではありませんが) を開き、
MyNodeのインスタンスを選択して、プロパティーシートの date プロパティーの値をクリックします。date picker のポップアップが表示され、ちゃんと動作することを確認してください:
DatePropertyEditor をグローバル登録する
プロパティーエディタを同じ型のすべてのプロパティーに対して使えるように登録しておくと便利な場合が多いでしょう。実際、 DatePropertyEditor は通常、 java.util.Date 型のどんなプロパティーにも有用です。このようなプロパティーエディタを登録すべきか決めるのに有用性が第1の要件ではありませんが、もしアプリケーションやモジュールが定期的に Date プロパティーを扱うのなら、登録したほうがよいでしょう。
それでは、 java.util.Date 型のすべてのプロパティーがプロパティーシートでエディタとして利用できるように DatePropertyEditor を登録する方法をご紹介します:
- My Editor プロジェクトを右クリックし、ポップアップメニューから 「プロパティー」を選択します。
- 「プロジェクトプロパティー」ダイアログの「ライブラリ」カテゴリの「依存関係を追加」をクリックします。ここで追加しなくてはならないのは、モジュールシステム API への依存関係です。そうすれば
ModuleInstallのサブクラスを作成し、スタートアップ時にいくつかのコードを実行することができるからです。ダイアログにModuleInstallと入力します。ダイアログは自動的に「モジュールシステム API」を選択するはずです。Enter キーを押すか「了解」をクリックし、 My Editor モジュールにモジュールシステム API への依存関係を追加します。 - My Editor プロジェクトの
org.myorg.myeditorパッケージを右クリックし、「新規」>「その他」を選択します。「モジュールの開発」カテゴリの「モジュールインストーラ」を選択し、「次へ」をクリックします。「完了」をクリックします。するとorg.openide.modules.ModuleInstallのサブクラスが作成されるでしょう。このクラスには、スタートアップ時に実行されるコードが含まれています。 - スタートアップ時に実行される
restored()メソッドを以下のように実装します:public void restored() { PropertyEditorManager.registerEditor(Date.class, DatePropertyEditor.class); }ここでは、java.util.Date型のすべてのプロパティーの、システムを通じたデフォルトエディタとして、DatePropertyEditorを登録しています。 - Ctrl-Shift-I キーを押してインポートを修正します。
本当に必要のある場合のみ ModuleInstall を使うということを忘れないでください。 <c1>ModuleInstall</c1> クラスは、スタートアップ時により多くのコードを実行することになるので、アプリケーションの起動を遅くしてしまいます。使わないですむなら使わないでください。もしどうしてもたくさんのプロパティーエディタを登録する必要があるなら、1つのモジュールに統合し、そこですべてのエディタを登録するのがよいでしょう。
独自の型に対してプロパティーエディタを作成したい場合は、登録コードをそのクラスがロードされた時に実行される静的プロックなどに埋め込むなどするとよいでしょう。
public class Foo {
static {
PropertyEditorManager.registerEditor(Foo.class, FooEditor.class);
}
//...
要注意: もし、プロパティーエディタが使用されるかどうか定かでないときは、
PropertyEditorManager.setEditorSearchPath()を使い、PropertyEditorManager.getEditorSearchPath()で取得するパッケージの配列にあなたのパッケージを追加した方がよいかもしれません。上のコードは、FooEditor.classをメモリにロードします。約1K は不必要なメモリを使用することになります。プロパティーエディタが1つや2つなら許されるでしょうが、それ以上ならプロパティーエディタを1つのパッケージにまとめ、各クラスに適当な名前をつけ、このパッケージを検索パスに登録したほうがよいでしょう。プロパティーエディタに関するより多くの情報については、PropertyEditorManagerの javadoc を参照してください。
プロパティーパネルの使用
詳細に取り扱わないにしても、プロパティーシートがNode.Property オブジェクトが役に立つ唯一の場所というわけではないということに言及しておいた方がよいでしょう。他にも PropertyPanel と呼ばれる便利な UI クラスが、 org.openide.explorer.propertysheet パッケージにあります。このクラスは、プロパティーシートのように、エディタ領域とカスタムエディタボタンと共に、1プロパティーを表示することができます。もしくは somePropertyPanel.setPreferences(PropertyPanel.PREF_CUSTOM_EDITOR) を呼べば、Property のカスタムエディタを表示することができます。プロパティーエディタがある設定/取得メソッドのペアを編集するのに適した UI コンポーネントを取得するために便利な方法として役に立ちます。
