2015-01-15 2 views
3

로드 된 처음 몇 초가 지난 후에도 잘 작동하는 내 자신의 Marquee 클래스/컨트롤을 만들었습니다. 그러나, 일단 응용 프로그램이 첫 번째로 부하를 걸면 부하는 maxWidth : 0.0 minWidth : 2.0이라고 가정합니다. 아래는 Marquee 클래스 코드 및 테스트 응용 프로그램에서로드하는 데 사용하는 코드입니다.JavaFX 컨트롤에서 초기화 즉시 localBounds를 인식하도록하려면 어떻게합니까?

움직이는 등급 :

public class Marquee extends HBox { 
     /** 
     * Add a node to the Marquee control 
     * @param node - a node to add 
     */ 
     public void add(Node node) { 
      getChildren().add(node); 
     } 

     /** 
     * Add a list of nodes to the Marquee control 
     * @param observable list - an observable list to add 
     */ 
     public void addAll(ObservableList<Node> list) { 
      getChildren().addAll(list); 
     } 

     /** 
     * Default Constructor: Initializes the Marquee Object with default settings: 
     * Empty Array List of Nodes, initial delay, Direction.LEFT, Duration.seconds(10), Interpolator.LINEAR, 10) 
     */ 
     public Marquee() 
     { 
      this(FXCollections.observableArrayList(new ArrayList<Node>()), Duration.seconds(3), Direction.LEFT, Duration.seconds(10), Interpolator.LINEAR, 10.0); 
     } 

     /** 
     * Constructor: Initializes the Marquee Object with default settings 
     * @param observable list 
     */ 
     public Marquee(ObservableList<Node> nodes) 
     { 
      this(nodes, Duration.seconds(3), Direction.LEFT, Duration.seconds(10), Interpolator.LINEAR, 10.0); 
     } 

     /** 
     * Constructor: Initializes the Marquee Object with default settings 
     * @param observable list 
     * @param duration - usually in seconds i.e. Duration.seconds(10) 
     */ 
     public Marquee(ObservableList<Node> nodes, Duration duration) { 
      this(nodes, Duration.seconds(3), Direction.LEFT, duration, Interpolator.LINEAR, 10.0); 
     } 

     /** 
     * Constructor: Initializes the Marquee Object with default settings 
     * @param observable list 
     * @param direction - an enum, i.e Direction.LEFT or Direction.RIGHT 
     * @param duration - usually in seconds i.e. Duration.seconds(10) 
     */ 
     public Marquee(ObservableList<Node> nodes, Direction direction, Duration duration) { 
      this(nodes, Duration.seconds(3), direction, duration, Interpolator.LINEAR, 10.0); 
     } 

     /** 
     * Constructor: Initializes the Marquee Object with default settings 
     * @param observable list 
     * @param duration - usually in seconds i.e. Duration.seconds(10) 
     * @param interpolator - effects the translation behavior, i.e 
     * Interpolator.EASE_BOTH, or EASE_LINEAR 
     */ 
     public Marquee(ObservableList<Node> nodes, Duration duration, Interpolator interpolator) 
     { 
      this(nodes, Duration.seconds(3), Direction.LEFT, duration, interpolator, 10.0); 
     } 

     /** 
     * Constructor: Initializes the Marquee Object with default settings: 
     * @param observable list 
     * @param initialDelay - the amount of time before the marquee will begin scrolling 
     * after the application has loaded 
     * @param direction - an enum, i.e Direction.LEFT or Direction.RIGHT 
     * @param duration - usually in seconds i.e. Duration.seconds(10) 
     * @param interpolator - effects the translation behavior, i.e 
     * Interpolator.EASE_BOTH, or EASE_LINEAR 
     */ 
     public Marquee(ObservableList<Node> list, Duration initialDelay, Direction direction, Duration duration, Interpolator interpolator) { 

      this(list, initialDelay, direction, duration, interpolator, 10.0); 
     } 

     /** 
     * Preferred Constructor: Initializes the Marquee Object with your preferred settings 
     * 
     * @param observable list 
     * @param initialDelay - the amount of time before the marquee will begin scrolling 
     * after the application has loaded 
     * @param direction - an enum, i.e Direction.LEFT or Direction.RIGHT 
     * @param duration - usually in seconds i.e. Duration.seconds(10) 
     * @param interpolator - effects the translation behavior, i.e 
     * Interpolator.EASE_BOTH, or EASE_LINEAR 
     * @param nodeSpacing - a double value that determines how far apart 
     * each element in the marquee will be placed from one another 
     */ 
     public Marquee(ObservableList<Node> list, Duration initialDelay, Direction direction, Duration duration, Interpolator interpolator, double nodeSpacing) { 
      super(); 
      getChildren().addAll(list); 
      setSpacing(nodeSpacing); 
      delay = initialDelay; 
      this.direction = direction; 
      this.duration = duration; 
      this.interpolator = interpolator; 
     } 

     public enum Direction { 
      LEFT, RIGHT 
     }; 

     private Direction direction; 
     private TranslateTransition animation; 
     private Duration duration; 

     /** 
     * This begins the animation of the Marquee. By default this method 
     * calculates the width of the Marquee's parent and uses that as its 
     * start point. When the nodes inside the Marquee have reached the outer 
     * bounds of its parent the Marquee will stop and reset. Note: If the 
     * application is resized, the animation will need to be stopped and 
     * restarted. The Marquee will recalculate its translation requirements each 
     * cycle so if the user resizes it's parent, the Marquee will conform. 
     * 
     * @param duration 
     *   the amount of time the translation should take 
     */ 
     public void animate() { 

      animation = new TranslateTransition(duration, this); 
      double maxWidth = getBoundsInLocal().getMaxX(); 
      double minWidth = getBoundsInLocal().getMinX() - getContentsWidth(); 

      switch (direction) { 
      case LEFT: 
       animation.setToX(minWidth); 
       animation.setFromX(maxWidth); 
       break; 
      case RIGHT: 
       animation.setToX(maxWidth); 
       animation.setFromX(minWidth); 
       break; 
      default: 
       animation.setToX(minWidth); 
       animation.setFromX(maxWidth); 
       break; 
      } 

      animation.setCycleCount(1); 
      animation.setInterpolator(getInterpolator()); 
      animation.setDelay(delay); 
      animation.playFromStart(); 

      animation.setOnFinished(new EventHandler<ActionEvent>() { 
       @Override 
       public void handle(ActionEvent event) 
       { 
        stopAnimation(); 
        recycleAnimation(); 
       } 

      }); 
     } 

     private Duration delay; 
     public Duration getDelay() { 
      return delay; 
     } 

     public void setDelay(Duration delay) { 
      this.delay = delay; 
     } 

     private Interpolator interpolator; 

     /** 
     * How the Marquee transitions its content into and out of FOV. 
     * Options are: 
     * DISCRETE (DO NOT USE), EASE_IN, EASE_OUT, EASE_BOTH, and LINEAR (DEFAULT). 
     * Any change to the Interpolator will take affect after the current cycle 
     * ends. 
     * Suggested Usage: setInterpolator(Interpolator.LINEAR) 
     */ 
     public void setInterpolator(Interpolator interpolator) 
     { 
      this.interpolator = interpolator; 
     } 

     /** 
     * The Interpolator of the Marquee. 
     * @return Interpolator 
     */ 
     public Interpolator getInterpolator() 
     { 
      return interpolator; 
     } 

     public void recycleAnimation() 
     { 
      setDelay(Duration.ZERO); 
      animate(); 
     } 

     /** 
     * Stop animation of Marquee 
     */ 
     public void stopAnimation() { 
      animation.stop(); 
     } 

     /** 
     * Set the default spacing between nodes in the Marquee Default is set to 
     * 5.0 
     */ 
     public void setNodeSpacing(double value) { 
      setSpacing(value); 
     } 

     /** 
     * Get the current spacing between nodes in the Marquee 
     * 
     * @return double 
     */ 
     public double getNodeSpacing() { 
      return getSpacing(); 
     } 

     private int getContentsWidth() 
     { 
      int width = 0; 

      for(Node node : getChildrenUnmodifiable()) 
      { 
       width += node.boundsInLocalProperty().get().getWidth(); 
      } 

      return width; 
     } 

    } 

내 메인 클래스

public class Main extends Application { 
    @Override 
    public void start(Stage primaryStage) { 
     try { 

      BorderPane root = new BorderPane(); 

      ObservableList<Node> labels = FXCollections.observableArrayList(); 
      labels.add(new Label("Test Label 1")); 
      labels.add(new Label("Test Label 2")); 

      Marquee marqueeLeft = new Marquee(labels, Duration.ZERO, Direction.LEFT, Duration.seconds(10), Interpolator.EASE_BOTH, 10.0); 
      root.setTop(marqueeLeft); 

      final ObservableList<Node> labels2 = FXCollections.observableArrayList(); 
      labels2.add(new Label("Test Label 3")); 
      labels2.add(new Label("Test Label 4")); 
      final Marquee marqueeRight = new Marquee(labels2, Duration.ZERO, Direction.RIGHT, Duration.seconds(10), Interpolator.EASE_BOTH, 10.0); 
      root.setBottom(marqueeRight); 
      marqueeLeft.animate(); 
      marqueeRight.animate(); 

      Button button = new Button(); 
      button.setOnAction(new EventHandler<ActionEvent>() { 
       @Override 
       public void handle(ActionEvent event) { 
        System.out.println("Workin"); 
        marqueeRight.add(new Label("Test Add Label")); 
       } 
      }); 

      root.setCenter(button); 

      Scene scene = new Scene(root,600,300); 
      scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 
      primaryStage.setScene(scene); 
      primaryStage.show(); 


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

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

나는 무대를 보여주고 parentBounds이 localBounds, 당신은 그것을 이름을 얻으려고 때까지 천막을로드 기다리고 노력했다. 0.0, 2.0부터 항상 시작하고 싶습니다.

모든 조언을 주시면 감사하겠습니다. 당신의 Marquee 클래스에서

답변

2

, 당신은 단지 각 노드의 폭을 얻는다 getContentWidth() 호출 노드 애니메이션하려면 : 1

private int getContentsWidth(){ 
    int width = 0; 
    for(Node node : getChildrenUnmodifiable()){ 
     width += node.boundsInLocalProperty().get().getWidth(); 
    } 
    return width; 
} 

당신이 당신의 윤곽을 인스턴스화, 그리고 애니메이션을 시작합니다

Marquee marqueeLeft = new Marquee(labels, Duration.ZERO, Direction.LEFT, Duration.seconds(10), Interpolator.EASE_BOTH, 10.0); 
marqueeLeft.animate(); 

그리고 다음 당신은 무대를 보여줍니다.

접근 방식의 문제는 getWidth()에 있습니다. 항상 을 반환하고까지 노드를 표시하고 노드를 레이아웃합니다. 그 후에는 너비가 0이되지 않습니다.

이제 첫 번째 애니메이션주기를 기다려야합니다.

public void recycleAnimation(){ 
    setDelay(Duration.ZERO); 
    animate(); 
} 

이 두 번째 시간, 모든 노드가 무대이며, 그들은 유효한 폭을 가지고 있고, 천막은 이동 시작 : 애니메이션이 끝날 때 다시 animate()를 호출합니다.

해결 방법 : 그냥 애니메이션 전화 이동 :

Scene scene = new Scene(root,600,300); 
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 
primaryStage.setScene(scene); 
primaryStage.show(); 

marqueeLeft.animate(); 
marqueeRight.animate(); 

그리고 지금은, 샘플에서, 폭이 126 픽셀이 될 것를하고 윤곽이 예상대로 움직이기 시작합니다.

스테이지가 표시되는 윤곽이 후 을 만든 경우

편집이 : animate()에 대한 호출이 후 inmediately 를 발생하기 때문에

Scene scene = new Scene(root,600,300); 
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm()); 
primaryStage.setScene(scene); 
primaryStage.show(); 

Marquee marqueeLeft = new Marquee(labels, Duration.ZERO, Direction.LEFT, Duration.seconds(10), Interpolator.EASE_BOTH, 10.0); 
root.setTop(marqueeLeft); 
marqueeLeft.animate(); 

이이 중 하나가 작동하지 않습니다 노드가 장면에 추가되고 JavaFX 응용 프로그램 스레드에 값을 업데이트 할 시간이 없습니다. JavaFX 구조 here에 대해 읽을 수 있습니다.

솔루션 : 그 작업을 수행하는 데 시간이 그래프 장면을 보자

root.setTop(marqueeLeft); 
root.setBottom(marqueeRight); 

Platform.runLater(new Runnable() { 

    @Override 
    public void run() { 
     marqueeLeft.animate(); 
     marqueeRight.animate(); 
    } 
}); 
+0

감사합니다! 스테이지를 보여준 후에로드하려고했을 때 나는 거기에서 모든 인스턴스화도하고 있었다고 생각합니다. – JeramyRR

+0

나는 왜 그것이 효과가 없었고 유효한 해결책인지에 대한 적절한 설명으로 나의 대답을 편집했다. –

+0

다시 한번 감사드립니다! 이것은 진정한 도움이되었습니다. 이제 레이블을 초과하는 것을 방지하는 방법을 알아낼 수 있다면 ... – JeramyRR

1

을 당신은 당신이 자바를 사용하는 경우 (호세 페레의 대답 또는 당신을 제안 표준 펄스 과정의 동안 자바 FX는 암시 적으로 레이아웃 패스를 처리 할 수 ​​있도록 8+) 할 수 있습니다 manually applyCSS trigger a layout pass :

Pane parentNode = new Pane(); 
Scene scene = new Scene(parentNode); 
Node child = new Button("XYZZY"); 
parentNode.getChildren().add(child); 

System.out.println(button.getWidth()); // outputs 0 as width is not yet known. 

parentNode.applyCss(); 
parentNode.layout(); 

System.out.println(button.getWidth()); // outputs non-0 as width is now known. 
+1

그래도 문제는 없지만 질문에 Java-7 태그가 붙어 있기 때문에 OP가 JavaFX 8을 사용하고 있는지 확신 할 수 없습니다. –

+0

Java-7 태그를 놓친 것, 예를 들어 applyCss 메소드 Java 7에서는 사용할 수 없습니다. – jewelsea

+0

예, 현재 Java 7을 사용합니다. Java 8로 점프하면이 점을 명심하십시오. – JeramyRR

관련 문제