今回は、前回作成した「MonolineFunction」 MVC グループを、それまで作成していた「FunctionPlotter」 MVC グループに埋め込みます(一覧)。
今回扱う埋め込みはアプリケーション開始時に行われ、アプリケーションが実行状態になればあまり他の部分との違いはありません(少なくとも View の挙動は)。 気持ち的には、埋め込むというより、もともとのアプリケーションが複雑になってきたので1つの MVC グループに切り出したといった感じでしょうか。 正確ではありませんが「コンポジション」とか「静的」といったイメージの埋め込みです。
この埋め込みに必要なコードは
の2つ。
FunctionPlotterModel.groovy
とその前に、埋め込みには関係ありませんが、前回 FunctionPlotterModel のいくつかのプロパティを MonolineFunctionModel へ切り出したので、切り出された方の FunctionPlotterModel の方を載せておきます:
package functionplotter class FunctionPlotterModel { @Bindable double from @Bindable double to @Bindable double min @Bindable double max def copyProperties(){ return [from:from, to:to, min:min, max:max] } }
残ったプロパティはグラフを描画する変域に関するものだけです。 copyProperties() メソッドはグラフ作成を別スレッドで行うためにプロパティ値をコピーするメソッド。
FunctionPlotterView.groovy
では気を取り直して、埋め込みの方法を見ていきましょう。 まずは View の埋め込み。 Griffon 0.9.5 以降を使用している場合は「追記」を参照。
埋め込まれる側の MonolineFunctionView はルート・コンポーネントを JPanel として作成しましたが、FunctionPlotterView にはそれを子コンポーネントとして付加されるコンポーネントを用意する必要があります。 ここでは 'functionSection' と id を付けたパネル (JPanel) にします。 コードはこんな感じ:
package functionplotter ... // actions など application(...){ ... // メニューバー panel(border:emptyBorder(6)){ migLayout() panel(id:'controlSection', constraints:'west', border:emptyBorder(3)){ migLayout() // この JPanel の子コンポーネントとして MonolineFunctionView を埋め込む panel(id:'functionSection'){ migLayout(layoutConstraints:'wrap 1') widget(app.views.f.content) // MonolineFunction の View を埋め込むコード } ... // 'Paint All' ボタン } ... // チャート描画部分 } }
ちょっとゴチャゴチャしてますが、アプリケーションの実行結果で説明するとこうなってます:
- controlSection パネルは埋め込みには特に関係ありません。(じゃあ書くなって?)
- また、MonolineFunctionView を埋め込むために作った functionSection パネルは、今回行う埋め込みには id を指定する必要はありません(説明と今後のため id を指定しました)。
埋め込みを行うコードは
widget(app.views.f.content)
の部分です。 widget() は親コンポーネントに引数のコンポーネントを付加するメソッドで、お呪い(?)みたいなものです。 引数の部分は要説明:
app | GriffonApplication オブジェクト。 |
views | GriffonApplication 内に存在する View 全て*1の Map。 |
f | MVC グループのインスタンスを特定する識別子(以下の FunctionPlotterController 内で指定)。 |
content | MonolineFunctionView 内で指定した id。 |
となってます。 ある MVC グループ内のコードから他の MVC グループ内のオブジェクト、プロパティなどにアクセスする場合には主にこの方法を用いるようなので、このアクセス方法は超重要!
ちなみにこんな感じに View にアクセスすることもできます*2:
widget(app.groups.f.view.content)
FunctionPlotterController.groovy
残るは Controller の処理。 Controller では mvcGroupInit() メソッド内に、埋め込む MVC グループを生成するコード (createMVCGroup()) を書きます*3(Griffon 0.9.5 以降を使用している場合は「追記」を参照)。 FunctionPlotterController のコードはこんな感じ:
package functionplotter class FunctionPlotterController { def model def view void mvcGroupInit(Map args) { createMVCGroup('MonolineFunction', 'f', [name:'f(x)']) } ... }
- mvcGroupInit() メソッドは(この Controller が属する)MVC グループのインスタンスが作成されたときに呼び出されます。 返り値は void。 一部ドキュメントでは def で宣言しているものもありますが、Griffon 0.9.2-beta-3 ではコンパイル・エラーになります*4。
- createMVCGroup() メソッドに関して
第3引数の初期化パラメータは次の2つに使われているようです:
- Controller (今の場合は MonolineFunctionController)などに mvcGroupInit() メソッドを定義した場合、そのパラメータとして渡される。
- Model, View, Controller のプロパティに Map のキーと一致するものがあれば、その値でプロパティを初期化する。
今の場合、[name:'f(x)'] をパラメータとして渡していますが、これによって MonolineFunctionModel の「name」プロパティが 'f(x)' によって初期化されるようです。 同様のことは View でもできるようで、id:masanobuimai さん「MVC Groupの切り替えを分かった気になってやってみた。」の記事中で DetailPanelView の rootPane などがパラメータの値で初期化されてます。
mvcGroupInit() メソッド
mvcGroupInit() の引数で渡されるパラメータは、デフォルトで以下のようなエントリーがセットされています:
パラメータ名 | パラメータ値(上記の例での) |
---|---|
mvcType | 'MonolineFunction' (グループ名) |
mvcName | 'f' (groupId) |
app | griffon.swing.SwingApplication オブジェクト |
model | MonolineFunctionModel オブジェクト |
view | MonolineFunctionView オブジェクト |
controller | MonolineFunctionController オブジェクト |
builder | org.codehaus.griffon.runtime.builder.UberBuilder オブジェクト |
execSync | |
execAsync | |
execOutside | |
execFuture | |
isUIThread |
これらの値もプロパティ(フィールド)の値の初期化に使えます:
package functionplotter class FunctionPlotterController { def mvcName // 他の設定をしなくても 'f' がセットされる(上記の例で) ... }
createMVCGroup() メソッド
最後に createMVCGroup() メソッドに関して軽くまとめておきます。 createMVCGroup() メソッドはいくつかオーバーロードされています:
createMVCGroup(String groupName) createMVCGroup(String groupName, String groupId) createMVCGroup(String groupName, Map params) createMVCGroup(String groupName, String groupId, Map params)
シグニチャをまとめると下表の通り:
引数 | 型 | 必須 | 説明 |
---|---|---|---|
groupName | String | Yes | MVC グループの名前。 Application.groovy に登録されている名前。 |
groupId | String | No | グループ ID。 MVC のインスタンスにアクセスする際に指定するキーとして使う。 |
params | Map | No | MVC インスタンスの初期化に使用するパラメータ。 |
また、返り値は生成された Model, View, Controller の List です(順序もこの通り)。 使う場合は以下のように使うのが通例のようです:
def (m, v, c) = createMVCGroup('MonolineFunction', 'f', [name:'f(x)'])
結局のところ
何か思ったよりも説明がゴチャゴチャしちゃいましたが、MVC グループをコーディングしてしまえば、埋め込むのに必要なのはほんの数行だけです:
// FunctionPlotterView.groovy widget(app.groups.f.view.content) // FunctionPlotterController.groovy void mvcGroupInit(Map args) { createMVCGroup('MonolineFunction', 'f', [name:'f(x)']) }
追記
Griffon 0.9.5 では上記のコードではうまくいかないようです。 FunctionPlotterView で widget() ノードによって MonolineFunction グループの view を埋め込む時点で、まだこの MVC グループがインスタンス化されていないのが原因のようです(これを考えると、以前にこのコードがなぜ動いてたか疑問でもありますが・・・)。 幸い数行のコード変更でこれを修正できます。 やることは MonolineFunction の view の埋め込みを FunctionPlotterController で行うだけです。
// FunctionPlotterView.groovy application(...){ ... panel(id:'functionSection'){ migLayout layoutConstraints:'wrap 1' // 上記のサンプルではここに widget() ノードを書いてましたが、それを削除 } } // FunctionPlotterController.groovy void mvcGroupInit(Map args) { def (m, v, c) = createMVCGroup('monolineFunction', 'f', name:'f(x)') view.functionSection.add(v.content) // Controller から埋め込みを実行 }
埋め込み処理を書いているコードは FunctionPlotterController です。 MonolineFunctionController ではありません。 ここでやっていることは、次回に見る「アプリケーション実行中に MVC グループを作成して埋め込む」方法の、埋め込み処理を mvcGroupInit() メソッド内でやっているだけです。
- 作者: Andres Almiray,Danno Ferrin,James Shingler
- 出版社/メーカー: Manning Pubns Co
- 発売日: 2012/06/28
- メディア: ペーパーバック
- クリック: 8回
- この商品を含むブログ (19件) を見る
*1:groupId を指定せずに生成した MVC グループは登録されているかどうか確かめていませんが、アクセスするなら groupId を指定しておくべきでしょう。
*2:groups プロパティは GriffonApplication 内に存在する MVC グループ全てを Map として返します。 キーは各 MVC グループを生成したときの groupId
*3:MVC グループを生成するには buildMVCGroup() メソッドも使えますが、こちらは MVC 以外に actions や dialogs などのメンバがある場合に使うそうです。 機会があればそのうちに。
*4:おそらく、Griffon の最近のバージョンで Controller などのクラスが Java クラスを継承するように変更されたためかと。