2016-09-11 5 views
2

私は、コントローラ内Spinnerを持っている:Spinnerが最初に初期化されたときにバインドされたプロパティを更新しないのはなぜですか?

@FXML 
private Spinner<Integer> spnMySpinner; 

とコントローラでSimpleIntegerProperty

private static final SimpleIntegerProperty myValue = 
new SimpleIntegerProperty(3); //load a default value 

私は、コントローラのinitialize方法でそれらを一緒にバインドされています:

spnMySpinner.getValueFactory().valueProperty().bindBidirectional(myValueProperty().asObject()); 

しかしバインディングの仕事コントローラが2回目の初期化後にのみ正しくここで私はそれを再現することができます方法は次のとおりです。

  1. 私は関連するコントローラと、ステージを開く、それが正しくmyValueプロパティ(数3)で指定されたデフォルト値をロードします。
  2. 私はそれ4.作るためにスピナーの増分ボタンをクリックしますこれは、スピナーのvalueプロパティの値を変更しますが、バウンドプロパティmyValueは、私は、ステージ/ウィンドウを閉じる番号3.
  3. にそのまま残されています。
  4. 私は再び開き、スピナーの値は3です。
  5. もう一度インクリメントします。 Boomは現在バインディングが動作しており、スピンナーとバインドされたプロパティの両方に「4」があります。

最小限の全体が、発射可能/再現可能なコード:

Main.java:

package spinnerpoc; 

import java.io.IOException; 
import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Main extends Application { 

    @Override 
    public void start(Stage stage) throws IOException { 
     Parent root = FXMLLoader.load(getClass().getResource("MainWindow.fxml")); 
     Scene scene = new Scene(root); 
     stage.setScene(scene); 
     stage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 

} 

MainWindow.fxml:

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

<?import javafx.scene.control.Button?> 
<?import javafx.scene.layout.AnchorPane?> 


<AnchorPane fx:id="myRoot" id="AnchorPane" prefHeight="231.0" prefWidth="337.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="spinnerpoc.MainWindowController"> 
    <children> 
     <Button fx:id="btnOpenSpinnerWindow" layoutX="102.0" layoutY="103.0" mnemonicParsing="false" text="Open SpinnerWindow" onAction="#onOpenSpinnerWindow"/> 
    </children> 
</AnchorPane> 

MainWindowController.java:

package spinnerpoc; 

import java.io.IOException; 
import java.net.URL; 
import java.util.ResourceBundle; 
import javafx.event.ActionEvent; 
import javafx.fxml.FXML; 
import javafx.fxml.FXMLLoader; 
import javafx.fxml.Initializable; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.layout.AnchorPane; 
import javafx.stage.Modality; 
import javafx.stage.Stage; 

public class MainWindowController implements Initializable { 

    @FXML 
    private Button btnOpenSpinnerWindow; 
    @FXML 
    private AnchorPane myRoot; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
    } 

    @FXML 
    private void onOpenSpinnerWindow(ActionEvent event) throws IOException{ 
     FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("SpinnerWindow.fxml")); 
     Parent root = (Parent) fxmlLoader.load(); 
     Stage stage = new Stage(); 
     stage.initOwner(myRoot.getScene().getWindow()); 
     stage.initModality(Modality.WINDOW_MODAL); 
     stage.setTitle("SpinnerWindow"); 
     stage.setScene(new Scene(root)); 
     stage.show(); 
    } 

} 

SpinnerWindow.fxml:

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

<?import javafx.scene.control.Button?> 
<?import javafx.scene.control.Label?> 
<?import javafx.scene.control.RadioButton?> 
<?import javafx.scene.control.ScrollPane?> 
<?import javafx.scene.control.Slider?> 
<?import javafx.scene.control.Spinner?> 
<?import javafx.scene.control.SpinnerValueFactory.DoubleSpinnerValueFactory?> 
<?import javafx.scene.control.TitledPane?> 
<?import javafx.scene.control.ToggleGroup?> 
<?import javafx.scene.layout.HBox?> 
<?import javafx.scene.layout.VBox?> 


<ScrollPane xmlns:fx="http://javafx.com/fxml/1" fitToWidth="true" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.60" fx:controller="spinnerpoc.SpinnerWindowController"> 
    <content> 
     <VBox maxWidth="1.7976931348623157E308"> 
      <children> 
       <Spinner fx:id="spnMySpinner" editable="true" prefWidth="50.0" max="10" min="1" /> 
      </children> 
     </VBox> 
    </content> 
</ScrollPane> 

SpinnerWindowController.java:私は何を

package spinnerpoc; 

import java.net.URL; 
import java.util.ResourceBundle; 
import javafx.beans.property.SimpleIntegerProperty; 
import javafx.fxml.FXML; 
import javafx.fxml.Initializable; 
import javafx.scene.control.Spinner; 

public class SpinnerWindowController implements Initializable { 

    private static final SimpleIntegerProperty myValue = new SimpleIntegerProperty(3); 

    public static SimpleIntegerProperty myValueProperty() { 
     return myValue; 
    } 

    public static Integer getMyValue() { 
     return myValue.getValue(); 
    } 

    public static void setMyValue(int value) { 
     myValue.set(value); 
    } 

    @FXML 
    private Spinner<Integer> spnMySpinner; 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     spnMySpinner.getValueFactory().valueProperty().bindBidirectional(myValueProperty().asObject()); 
    } 

} 

(。BitBucket repoのコードも利用可能)

をしないのですか?

+0

それはあなたが与えられている情報から知ることは不可能です。問題を再現する[MCVE]を作成し、質問を組み込むために質問を編集します。 –

+0

@ James_D私は簡単な例を書いたが、それは100行以上あるので、私はそれをbitbucketにプッシュした。 – Leprechaun

+0

質問にはあまりにも多くのコードが含まれていません。例をあなたの質問に移しました。 –

答えて

1

"早すぎるガベージコレクション"問題が発生しています。このhereの説明を参照してください。失敗したスピナーを表示するたびに必ずしもそうとは限りませんが、散発的なものであり、その動作はマシンごとに異なると考えられます。 JVMが使用できるメモリを制限すると、動作しないことがあります。

あなたがIntegerProperty.asObject()を呼び出すと、それ

が双方向このIntegerPropertyにバインドObjectPropertyを作成します。

は今結合双方向のthis feature to prevent accidental memory leaksを持っていることに注意してください。

のJavaFX双方向バインディング実装が弱いリスナーを使用します。つまり、双方向バインディングはプロパティのガベージコレクションを妨げません。

だから、あなたが明示的に作成結合双方向のは、それがごみ収集されてから(asObject()によって作成されたObjectProperty<Integer>)にバインドされている事を防ぐことはできません。あなたには参照がないので、SpinnerWindow Controllerinitialize()メソッドを終了するとすぐにガベージコレクションの対象となります。明らかに、あなたのスピナー値が双方向にバインドされている値がガベージコレクションされると、バインディングはそれ以上は機能しません。

デモ目的のために、ガベージコレクションを強制するためにフックを入れることでこれを確認できます。例えば。 SpinnerWindowControllerで

<ScrollPane onMouseClicked="#gc" xmlns:fx="http://javafx.com/fxml/1" ...> 
SpinnerWindow.fxmlで

@FXML 
private void gc() { 
    System.out.println("Invoking GC"); 
    System.gc(); 
} 

を行います。これを行うと、スクロールペインをクリックするとガベージコレクションが強制され、スピナー値を変更してもプロパティは更新されません。

は、この問題を解決あなたが asObject()から取得するプロパティへの参照を保持するには:

public class SpinnerWindowController implements Initializable { 

    private static final SimpleIntegerProperty myValue = new SimpleIntegerProperty(3); 

    public static SimpleIntegerProperty myValueProperty() { 
     return myValue; 
    } 

    public static Integer getMyValue() { 
     return myValue.getValue(); 
    } 

    public static void setMyValue(int value) { 
     myValue.set(value); 
    } 

    @FXML 
    private Spinner<Integer> spnMySpinner; 

    private ObjectProperty<Integer> spinnerValue = myValueProperty().asObject(); 

    @Override 
    public void initialize(URL url, ResourceBundle rb) { 
     spnMySpinner.getValueFactory().valueProperty().bindBidirectional(spinnerValue); 
    } 

} 
+0

ありがとうございます。これは多くのことを説明しますが、いくつかのケースでのみ役立ちます。問題を完全に取り除くのに役立つと思われるのは、 'spinnerValue'を静的フィールドとして持つことです。したがって、ウィンドウを閉じた後でもバインディングプロパティを保持します。私は推測する。 – Leprechaun

+0

実際、いいえ、それは意味をなさない。なぜ私はフィールドが静的になったときにだけ動作するのか分かりません。 – Leprechaun

+0

他のスコープの問題があるように聞こえます。そのフィールドを静的にすることは、おそらく非常に悪い考えです(コントローラーのすべてのインスタンスのスピナーを同じ値にバインドしたくない場合)。 –

関連する問題