web-dev-qa-db-ja.com

FXMLを使用するカスタムJavaFXコンポーネントを拡張する方法

ビューにFXMLを使用している場合、カスタムJavaFXコンポーネントを適切に拡張してGUIコンポーネントを追加または変更するにはどうすればよいですか?

シナリオ例として、カスタムJavaFXコンポーネントを作成するために次のアプローチを使用するとします。

SpecializedButton.Java(コントローラー)

package test;

import Java.io.IOException;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;

public class SpecializedButton extends HBox
{
    @FXML private Button button;
    @FXML private Label label;

    public SpecializedButton() 
    {
        FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );

        loader.setRoot( this );
        loader.setController( this );

        try {
            loader.load();
        } catch ( IOException e ) {
            throw new RuntimeException( e );
        }
    }

    // specialized methods for this specialized button
    // ...
    public void doSomething() {
        button.setText( "Did something!" );
    }
}

SpecializedButton.fxml(ビュー)

<?xml version="1.0" encoding="UTF-8"?>

<?import Java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>

<fx:root type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
   <children>
      <AnchorPane HBox.hgrow="ALWAYS">
         <children>
            <Label fx:id="label" text="Click to do something: " AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
         </children>
      </AnchorPane>
      <AnchorPane HBox.hgrow="ALWAYS">
         <children>
            <Button fx:id="button" onAction="#doSomething" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
         </children>
      </AnchorPane>
   </children>
</fx:root>

MyApp.Java

package test;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MyApp extends Application
{
    public static void main( String[] args )
    {
        launch( args );
    }

    public void start( Stage stage ) throws Exception
    {
        stage.setScene( new Scene( new SpecializedButton(), 300, 50 ) );
        stage.show();
    }
}

SpecializedButtonクラスは、FXMLビュー(SpecializedButton.fxml)をロードし、そのコントローラーとして機能します。

SpecializedButton FXMLビューは、2つのAnchorPanesと、左側と右側にそれぞれLabelとButtonを持つHBoxを作成するだけです。ボタンがクリックされると、SpecializedButtonコントローラークラスのdoSomething()が呼び出されます。


問題

この設定では、FXMLを使用してビューをアプリケーションロジックから分離しています。

ただし、SpecializedButtonを拡張する新しいHighlySpecializedButtonクラスを作成したい場合は、ビューを「拡張」する方法もわかりません。

SpecializedButtonビューを使用したいが、HighlySpecializedButton用に変更したい場合は、ビューコードを新しいFXMLファイルにコピーする必要があり、適切な継承ではなく重複したコードになります。

FXMLを使用しておらず、代わりにJavaクラス内でGUIコンポーネントを作成していた場合、そのクラスを拡張すると、スーパークラスからコンポーネントを適切に継承および追加/変更できるようになります。 。

私はJavaFXに関するいくつかの非常に重要な概念を明らかに誤解しています。


質問

この場合のFXMLビューは非常に「ダム」なので、カスタムコンポーネントから継承するたびに、まったく新しいFXMLビューを作成する必要がありますか?

または、実際の問題を誤解している場合、FXMLの有無にかかわらず、拡張可能なカスタムJavaFXコンポーネントを作成する適切な方法は何ですか?

洞察をいただければ幸いです。前もって感謝します!

11

私が理解していることから、既存のコンポーネントを拡張し、新しいコンポーネントのマークアップ全体をコピーして貼り付けることを避けたいと考えています。

_@DefaultProperty_アノテーションを使用して、コンポーネントの拡張ポイントを定義できます。 e.xを見てください。 _javafx.scene.layout.Pane_:ObservableList getChildren()で定義された@DefaultProperty("children")があります。このように、e.xをコンポーネントのルート要素としてHBox(ペインを拡張)として使用すると、すべての子コンポーネントがペインで定義されたchildrenプロパティに追加されます。

同様に、FXMLで拡張ポイントを定義できます。

SpecializedButton.fxml(この例では少し簡略化しました)

_<fx:root type="HBox">
  <HBox>
    <Label fx:id="label" text="Click to do something: " />
    <HBox fx:id="extension"></HBox>
  </HBox>

  <HBox>
    <Button fx:id="button" onAction="#doSomething"/>
  </HBox>
</fx:root>
_

_HBox fx:id="extension"_が私の拡張ポイントになります:

_@DefaultProperty(value = "extension")
public class SpecializedButton extends HBox
{
    @FXML private HBox extension;

    public ObservableList<Node> getExtension() {
        return extension.getChildren();
    }
    // ... more component specific code
}
_

SuperSpecializedButton.fxml

_<fx:root type="SpecializedButton">
  <Label text="EXTENDED HALLO"/>
</fx:root>
_

そして:

_public class SuperSpecializedButton extends SpecializedButton
{
    // ... more component specific code
}
_

ご覧のとおり、超特殊コンポーネントのfxmlを定義するだけで済みます。

このコードはJavaFX> = 2.1でのみ機能します 。以前は、スーパークラスのコンポーネントインジェクションはFXMLLoaderでは機能しませんでした。

Githubに実用的な例を追加しました: https://github.com/matrak/javafx-extend-fxml

6
mrak

これまでに行ったことはありませんが、新しいカスタムコンポーネントをインスタンス化するときに、Initializableインターフェイスを実装してビューコンポーネントをセットアップできます。

新しいコンポーネント(ラベル、ボタン、imgなど)を追加するには、ビューファイルのすべての子コンポーネントをAnchorPane、GridPane、VBox、またはプログラムでコンポーネントを追加したいコンポーネント内にラップします。 HBoxからクラスを拡張しているので、これはすでに実行できます。

これが私がすることです:

public class SpecializedButton extends HBox implements Initializable{

@FXML private Button button;
@FXML private Label label;

public SpecializedButton(){
    FXMLLoader loader = new FXMLLoader( getClass().getResource( "SpecializedButton.fxml" ) );

    loader.setRoot( this );
    loader.setController( this );

    try {
        loader.load();
    } catch ( IOException e ) {
        throw new RuntimeException( e );
    }
}

// specialized methods for this specialized button
// ...
public void doSomething() {
    button.setText( "Did something!" );
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    button.setText("Initialized Text");
}  
}

したがって、HighlySpecializedButtonクラスの場合:

public class HighlySpecializedButton extends SpecializedButton {



public HighlySpecializedButton (){
    super();
}

// HighlySpecializedButton methods


@Override
public void initialize(URL url, ResourceBundle rb) {
    this.getChildren().add(new Button("New Button"));

}  
}

これがあなたを助けることができることを願っています

0
Miguel Cardenas