2017-02-16 21 views
1

Я наткнулся на проблему, используя слайдер в JavaFX.
Создаю файл fxml с помощью Slider и добавьте к нему контроллер.
Внутри контроллера У меня есть DoubleProperty, который связывается с Ползунок «ы valueProperty. Затем, когда я хочу привязать это свойство из другого места, я привязываюсь к свойству, которое находится внутри контроллера (какой-то подход среднего человека см. Рис.).
enter image description here
Но когда я это делаю, он работает неправильно.Ошибка в JavaFX, привязавшись к среднему человеку по значению свойства ползунка?

Когда я пользуюсь слайдером, значения обновляются соответствующим образом некоторое время, но когда я его покажу, в какой-то момент он перестает обновлять привязку и отказывается делать это снова даже после отпускания и повторного нажатия.
Когда я удаляю свойство middle-man в контроллере и просто прохожу через valueProperty с ползунка напрямую, он работает.

Программа:
Main.java

public class Main extends Application{ 
    private MainController controller; 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     FXMLLoader loader = new FXMLLoader(); 
     loader.setLocation(getClass().getResource("main.fxml")); 
     Parent root = loader.load(); 
     controller = loader.getController(); 

     Scene scene = new Scene(root); 
     primaryStage.setScene(scene); 

     primaryStage.show(); 

     showSlider(); 
    } 

    private void showSlider() { 
     SliderShower sliderShower = new SliderShower(); 
     sliderShower.show(); 
     sliderShower.getSliderValueProp().addListener(((observable, oldValue, newValue) -> { 
      controller.setText(Double.toString((double)newValue)); 
     })); 
    } 

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

main.fxml

<?import javafx.scene.control.Label?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 

<GridPane prefHeight="100.0" prefWidth="100.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sliderBug.main.MainController"> 
    <columnConstraints> 
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> 
    </columnConstraints> 
    <rowConstraints> 
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
    </rowConstraints> 
    <children> 
     <Label fx:id="label" text="Label" /> 
    </children> 
</GridPane> 

MainController.java

public class MainController { 
    @FXML 
    private Label label; 

    public void setText(String text) { 
     label.setText(text); 
    } 
} 

SliderShower.java

public class SliderShower { 
    private Parent root; 
    private SliderShowerController controller; 
    private Stage stage; 
    private DoubleProperty sliderValueProp; 

    public SliderShower() { 
     sliderValueProp = new SimpleDoubleProperty(0); 
     FXMLLoader loader = new FXMLLoader(); 
     loader.setLocation(getClass().getResource("sliderShower.fxml")); 
     try { 
      root = loader.load(); 
      controller = loader.getController(); 
      stage = new Stage(); 
      stage.initModality(Modality.APPLICATION_MODAL); 
      stage.setScene(new Scene(root)); 

      sliderValueProp.bind(controller.getSliderValueProp()); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    public DoubleProperty getSliderValueProp() { 
     return sliderValueProp; // This does not work 
//  return controller.getSliderValueProp(); // This would work 
    } 

    public void show() { 
     stage.show(); 
    } 
} 

sliderShowerController.java

public class SliderShowerController { 
    @FXML 
    private Slider sliderUI; 

    DoubleProperty getSliderValueProp() { 
     return sliderUI.valueProperty(); 
    } 
} 

sliderShower.fxml

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

<?import javafx.scene.control.Slider?> 
<?import javafx.scene.layout.ColumnConstraints?> 
<?import javafx.scene.layout.GridPane?> 
<?import javafx.scene.layout.RowConstraints?> 

<GridPane prefHeight="100.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sliderBug.sliderShower.SliderShowerController"> 
    <columnConstraints> 
    <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> 
    </columnConstraints> 
    <rowConstraints> 
    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> 
    </rowConstraints> 
    <children> 
     <Slider fx:id="sliderUI" max="200.0" min="1.0" /> 
    </children> 
</GridPane> 

Вот ссылка на хранилище с изображением проблемы:
https://github.com/Chrisss50/sliderBug

Я делаю что-то неправильно или это просто ошибка?

Привет

+0

Вы можете создать пример, который вы можете разместить прямо в вопросе, вместо публикации ссылки на код? Если ссылка устаревает, вопрос будет бесполезен для кого-либо еще. –

+0

Да, вы правы. Я добавил всю программу. – Chriz

ответ

2

Наручники в JavaFX использовать слабые слушателей под капотом. Это предназначено для предотвращения утечек памяти, но имеет побочный эффект: если свойства, к которым что-то связано, выходят за пределы области видимости, привязка не предотвратит их сбор мусора и перестанет работать, если они есть. См. this question и this blog post.

Вы можете проверить это вопрос, добавив

root.setOnMouseClicked(e -> { 
    if (e.getClickCount() == 2) { 
     System.out.println("GC"); 
     System.gc(); 
    } 
}); 

к SliderShower конструктора. После этого двойной щелчок рядом с ползунком заставит сборку мусора, и привязка немедленно перестанет работать.

В вашем случае Main создает SliderShower как локальную переменную в методе showSlider, и, следовательно, он выходит за рамки, как только метод завершается. Один (несколько неестественно) исправление принудительно сохранить ссылку на SliderShower:

package sliderBug.main; 

import javafx.application.Application; 
import javafx.fxml.FXMLLoader; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 
import sliderBug.sliderShower.SliderShower; 

/** 
* Created by Christopher Juerges on 16/02/17. 
*/ 
public class Main extends Application{ 
    private MainController controller; 

    private SliderShower sliderShower ; 

    @Override 
    public void start(Stage primaryStage) throws Exception { 
     FXMLLoader loader = new FXMLLoader(); 
     loader.setLocation(getClass().getResource("main.fxml")); 
     Parent root = loader.load(); 
     controller = loader.getController(); 

     Scene scene = new Scene(root); 
     primaryStage.setScene(scene); 

     primaryStage.show(); 

     showSlider(); 
    } 

    private void showSlider() { 
     sliderShower = new SliderShower(); 
     sliderShower.show(); 
     sliderShower.getSliderValueProp().addListener(((observable, oldValue, newValue) -> { 
      controller.setText(Double.toString((double)newValue)); 
     })); 
    } 

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

Другой исправление заключается в использовании слушателя вместо связывания:

public SliderShower() { 
    sliderValueProp = new SimpleDoubleProperty(0); 
    FXMLLoader loader = new FXMLLoader(); 
    loader.setLocation(getClass().getResource("sliderShower.fxml")); 
    try { 
     root = loader.load(); 

     root.setOnMouseClicked(e -> { 
      if (e.getClickCount() == 2) { 
       System.out.println("GC"); 
       System.gc(); 
      } 
     }); 

     controller = loader.getController(); 
     stage = new Stage(); 
     stage.initModality(Modality.APPLICATION_MODAL); 
     stage.setScene(new Scene(root)); 

     // sliderValueProp.bind(controller.getSliderValueProp()); 

     controller.getSliderValueProp().addListener((obs, oldValue, newValue) -> 
      sliderValueProp.set(newValue.doubleValue())); 

    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
}