問題の声明
確かに、多くはSwingコンポーネントを使用してjava-beanの編集を定期的に実装する必要があります。 たとえば、さまざまなディレクトリ。 JDKは保守的であり、開発者の生活を大いに促進するような「すぐに使える」ものを提供しません。 いずれの場合も、次のような記述が必要です。
GridBagPanel panel = new GridBagPanel(){{ add( new JLabel( "Name" ), 0, 0 ); add( new JTextField( person.getName() ) {{ getDocument().addDocumentListener( new DocumentListener() { public void insertUpdate( final DocumentEvent e ) { person.setName( getText() ); } public void removeUpdate( final DocumentEvent e ) { person.setName( getText() ); } public void changedUpdate( final DocumentEvent e ) { person.setName( getText() ); } } ); }}, 0, 1 ); // ... }};
すでに多くのコードがあります。 ただし、ハンドラーに検証を追加する必要がある場合もあります。
あなたの人生を楽にするためのさまざまなアプローチがあります。
- IDEのフォームの「図面」。 たとえば、NetBeansでは、java.beansパッケージのツールを使用してフォームを構築し、ビューをデータに関連付けることができます。
Beanの変更を追跡するために、追加のコードが生成されます。 グラフィックコンポーネントを構築するために、コードも生成されます。 このコードにはすべて、肥大化して読み取り不能になるという特性があります。 ここでは、フォームを作成するプロセスについて説明します 。 - JGoodies(ユースケースはこちら )やMetawidget(ユースケースはこちら )などのリフレクティブフォーム生成のライブラリ。
原則として、バインディングはbinプロパティの文字列名に行きます。 したがって、リファクタリング中にバインディングの正確さが侵害される場合があります。
また、Metawidgetでは、コンポーネントの場所は間接的に設定されます。つまり、コンポーネントをパネルに直接追加する代わりに、たとえばXMLを指定する必要があります。
Metawidgetでは、たとえば、検証中にフレームを強調表示する必要がある他のパネルでコンポーネントをラップする方法も不明です。
自転車の発明に対する渇望に駆られて、私はあなたができるフォーム生成の最小限のライブラリを書くことにしました:
- 数行のコードでデフォルトのフォームを作成する
- すでに既製のコンポーネントがあるかのように、パネル内のコンポーネントの場所を通常制御する-実際には、Beanのプロパティに関連付けられたコンポーネントの注入
- 検証結果の制御。 インターフェイスを実装して検証処理を変更する
- インターフェイスを実装して、新しいタイプのコンポーネントを追加します
- 可能であれば、コンパイラのバインディングを確認してください
このライブラリを使用するコードがどのように見えるかを説明します
デフォルトのフォーム
ここではすべてが簡単です。クラスを与え、フォームを取得します。
Form<Person> form = FormBuilder.map( Person.class ).buildForm(); myFrame.add( form.asComponent() ); Person person = new Person(); person.setName( "john smith" ); // ... further initialization form.setValue( person );
コンポーネントの場所
おそらく、ケースの80%で、各フィールドに必要なコンポーネントは2つだけです。名前付きのラベルとエディター自体です。 古典的なアプローチはこれです。フィールドの名前を尋ね、コンポーネントを取得します。
Form<Person> form = FormBuilder.map( Person.class ).with( new PropertyNameBeanMapper<Person>() { @Override public JComponent mapBean( PropertyNameContext<Person> ctx ) { JPanel panel = new JPanel( new BorderLayout() ); panel.add( ctx.label( "name" ), BorderLayout.NORTH ); panel.add( ctx.editor( "name" ), BorderLayout.CENTER ); return panel; } } ).buildForm();
文字列をより信頼性の高いものに置き換えることは可能ですか? たとえば、次のように:
Form<Person> form = FormBuilder.map( Person.class ).with( new SampleBeanMapper<Person>() { @Override protected JComponent mapBean( Person sample, SampleContext<Person> ctx ) { Box box = Box.createHorizontalBox(); box.add( ctx.label( sample.getName() ) ); box.add( ctx.editor( sample.getName() ) ); return box; } } ).buildForm();
どのメソッドが呼び出されるかをコンテキストに伝えるサンプルとして動的プロキシを使用する場合( CGLIBライブラリの使用例 )、このコードは完全に機能します。 このアプローチはスレッドセーフではありませんが、GUIです。
マッピング
ctx.label()を使用すると、すべてが明確になります-JLabelが返されます。 そして、ctx.editor()は何を返すべきですか? コンテキスト内には、エディターがそのタイプに応じてフィールドごとに選択するマッピングが必要です。 しかし、少し拾います。 そして、それがカスタムコンポーネントである場合は? 設定が必要なポイントがいくつかあります。
- 値クラス
- コンポーネント作成操作
- 値の設定と取得
- コンポーネントでの変更の処理(検証の可能性あり)
たとえば、 サードパーティのカレンダーコンポーネントの場合、この構成は次のようになります。
class DateToDateChooserMapper implements TypeMapper<JDateChooser, Date> { public Class<Date> getValueClass() { return Date.class; } public JDateChooser createEditorComponent() { return new JDateChooser(); } public Date getValue( final JDateChooser editorComponent ) { return editorComponent.getDate(); } public void setValue(final JDateChooser editorComponent, final Date value) { editorComponent.setDate( value ); } public void handleChanges(final JDateChooser editorComponent, final ChangeHandler changeHandler) { editorComponent.getDateEditor() .addPropertyChangeListener( "date", new PropertyChangeListener() { public void propertyChange( final PropertyChangeEvent evt ) { changeHandler.onChange( BackgroundMarker.INSTANCE ); } } ); } }
ここで、changeHandlerは、コンポーネントの変更についてシステムに通知するのに役立ちます。 デフォルトでは、BeanはHibernate Validatorを使用して検証されます。 ValidationMarkerには、その結果(この場合はBackgroundMarker)が通知され、失敗したコンポーネントの外観を変更する方法が決定されます。
このタイプのすべてのフィールドにマッピングを指定する必要がある場合、コードは単純に見えます。
Form<Person> form = FormBuilder.map( Person.class ).use( new StringToTextAreaMapper() ).buildForm();
特定のフィールドについては、もう少し複雑です。
Form<Person> form = FormBuilder.map( Person.class ) .useForProperty( "description", new StringToTextAreaMapper() ) .buildForm();
繰り返しますが、動的プロキシを使用し、文字列ではなくメソッドにバインドして、型チェックを提供することは可能ですか? 多少面倒ですが、可能です:
Form<Person> form = FormBuilder.map( Person.class ).useForGetters( new GetterMapper<Person>() { @Override public void mapGetters( Person beanSample, GetterConfig config ) { config.use( beanSample.getDescription(), new StringToTextAreaMapper() ); } } ).buildForm();
おわりに
しばらく前にこのライブラリを書きましたが、今ではコミュニティに提出することにしました。 現在、私はこのライブラリのScala用アダプターを作成していますが、これまでのところかなり簡単なことがわかりました。 おそらく、次の投稿でこれを説明します。
ライブラリとMavenリポジトリの詳細な説明が記載されたウィキは、Google Code: code.google.com/p/swing-formbuilderにあります。
ソースをGitHubに転送しました: github.com/aeremenok/swing-formbuilder