2012-12-09 2 views
1

JavaFX 응용 프로그램이 있습니다. 약 20 개의 레이블이 있습니다. 사용자가 라벨을 클릭하면 빨간색으로 깜박이는 라벨이 필요합니다. 레이블을 빨간색으로 깜박이려면 실제로 스윙 용으로 개발했지만 JavaFX로 변환 된 스레드를 사용하고 있습니다. 그것은 잠시 동안 일 했었지만 최근에 응용 프로그램 잠금을 추적하여 레이블의 애니메이션을 추적했습니다.Java FX 스레드 안전 일시 중지/수면

new Thread(new AnimateLabel(tl,idx)).start(); 

TL 포인트 그것의 인덱스에 라벨의 ArrayList를하고, IDX에 : 나는 애니메이션을했던 방법은 간단했다. 다른 레이블에는 클릭 이벤트가 첨부되어 있으며이를 클릭하면 레이블에 애니메이션을 적용하는 스레드가 만들어집니다 (깜박입니다).

많은 이유 때문에 레이블을 많이 누르면 응용 프로그램이 잠길 수 있습니다.

저는 JavaFX의 스레드 안전성 문제라고 확신합니다.

TimerUpdater tu = new TimerUpdater(mDS); 
Timeline incHandler = new Timeline(new KeyFrame(Duration.millis(130),tu)); 
incHandler.setCycleCount(Timeline.INDEFINITE); 
incHandler.play(); 

TimerUpdater 끊임없이 라벨, 깜박이도 사람의 텍스트를 업데이트합니다 : 나는 그래서 자바 FX 애플리케이션 스레드를 공유하는 다른 스레드가 있습니다.

private class AnimateLabel implements Runnable 
{ 
    private Label lbl; 
    public AnimateLabel(Label lbl, int myIndex) 
    { 
     // if inAnimation.get(myIndex) changes from myAnim's value, stop, 
     // another animation is taking over 
     this.lbl = lbl; 
    } 

    @Override 
    public void run() { 
     int r, b, g; 
     r=255; 
     b=0; 
     g=0; 

     int i = 0; 
     while(b <= 255 && g <= 255) 
     { 
      RGB rgb = getBackgroundStyle(lbl.getStyle()); 
      if(rgb != null) 
      { 
       if(rgb.g < g-16) { return; } 
      } 
      lbl.setStyle("-fx-color: #000; -fx-background-color: rgb("+r+","+g+","+b+");"); 
      try { Thread.sleep(6); } 
      catch (Exception e){} 
      b += 4; 
      g += 4; 
      ++i; 
     } 
     lbl.setStyle("-fx-color: #000; -fx-background-color: fff;"); 
    } 
} 

나는 등이 실행됩니다 :

javafx.application.Platform.runLater(new AnimateLabel(tl, idx)); 

그러나, Thread.sleep를가 (6) 무시됩니다

다음은 라벨의 애니메이션입니다. javafx.application을 사용하여 스레드를 공유하면서 나중에 애니메이션의 속도를 제어하기 위해 잠시 멈출 수있는 방법이 있습니까?

안부, 더스틴

답변

4

나는 자바 FX 이벤트 큐가 작동하는 방법에 대한 약간의 오해가있는 것 같아요.

1) AnimateLabel 코드를 일반 스레드에서 실행하면 Label#setStyle(...) 코드가 해당 스레드에서 실행됩니다. 이는 불법이며 문제를 일으킬 수 있습니다.

2) 두 번째 예제에서와 같이 JavaFX 이벤트 대기열에서 AnimateLabel 코드를 완전히 실행하면 애니메이션이 완료 될 때까지 이벤트 스레드가 차단된다는 의미입니다. 한편, 응용 프로그램은 업데이트되지 않으며 사용자 이벤트를 처리하거나 다시 그려야합니다. 기본적으로 루프 내에서 레이블 스타일을 변경하지만 이벤트 대기열에 실제로 레이블을 다시 그리는 시간을 지정하지 않으므로 화면에 아무 것도 표시되지 않습니다.

준 정밀 접근은 두 가지가 혼합 된 것입니다. AnimateLabel을 별도의 스레드에서 실행하고 Label#setStyle(...)에 대한 호출을 Platform#runLater(...)에 랩핑하십시오. 이 방법을 사용하면 관련된 작업으로 이벤트 스레드에만 신경을 쓰고 다른 작업을 자유롭게 할 수 있습니다 (예 : UI 업데이트).

내가 말했듯이 이것은보다 정확한 방식으로 원하는 것을 수행 할 수있는 빌드 인 (build-in) 기능이 있기 때문에 반 정답입니다. 전환 클래스를 확인해 볼 수 있습니다. 커스텀 애니메이션을위한 간단한 접근 방식을 제공하며 노드의 가장 일반적인 속성을 애니 메이팅하기위한 미리 만들어진 서브 클래스를 제공합니다.

+0

난 그냥 모든 16millis 1 개 루프를 할 경우 Thread.sleep를 제거하고에 루프를 다시받을 생각 그것을 그대로 수용하면 효과가 있습니다. 시도 할 시간이 없지만 알려 드리겠습니다. Transition을 지적 해 주셔서 고맙습니다. 움직이는 데 사용 된 FX 클래스를 찾고있을 때 다시 찾지 못했습니다. 그리고 이것은 확실히 더 나은 접근 방법입니다. – SigSeg

3

사르 칸은 훌륭한 대답을 가지고 있습니다.

이 답변은 CSS 스타일을 수정하는 방법에 대한 Timeline 접근 방식에 대한 샘플 코드()를 제공합니다.코드는 질문에 게시하는 코드의 대안으로 사용할 수 있습니다.

이 접근법의 장점은 JavaFX 라이브러리가 모든 스레딩 문제를 처리하여 모든 코드가 JavaFX 스레드에서 실행되도록하고 스레드 안전 문제를 제거하는 것입니다.

import javafx.animation.*; 
import javafx.application.Application; 
import javafx.beans.property.*; 
import javafx.beans.value.*; 
import javafx.event.EventHandler; 
import javafx.scene.Scene; 
import javafx.scene.control.Label; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.TilePane; 
import javafx.stage.Stage; 
import javafx.util.Duration; 

public class CssFlash extends Application { 

    private Label flashOnClick(final Label label) { 
     label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;")); 

     DoubleProperty opacity = new SimpleDoubleProperty(); 
     opacity.addListener(new ChangeListener<Number>() { 
      @Override public void changed(ObservableValue<? extends Number> source, Number oldValue, Number newValue) { 
       label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: rgba(255, 0, 0, %f)", newValue.doubleValue())); 
      } 
     }); 

     final Timeline flashAnimation = new Timeline(
       new KeyFrame(Duration.ZERO, new KeyValue(opacity, 1)), 
       new KeyFrame(Duration.millis(500), new KeyValue(opacity, 0))); 
     flashAnimation.setCycleCount(Animation.INDEFINITE); 
     flashAnimation.setAutoReverse(true); 

     label.setOnMouseClicked(new EventHandler<MouseEvent>() { 
      @Override public void handle(MouseEvent t) { 
       if (Animation.Status.STOPPED.equals(flashAnimation.getStatus())) { 
        flashAnimation.play(); 
       } else { 
        flashAnimation.stop(); 
        label.setStyle(String.format("-fx-padding: 5px; -fx-background-radius: 5px; -fx-background-color: lightblue;")); 
       } 
      } 
     });   

     return label; 
    } 

    @Override public void start(Stage stage) throws Exception { 
     TilePane layout = new TilePane(5, 5); 
     layout.setStyle("-fx-background-color: whitesmoke; -fx-padding: 10;"); 

     for (int i = 0; i < NUM_LABELS; i++) { 
      layout.getChildren().add(flashOnClick(new Label("Click to flash"))); 
     }  

     Scene scene = new Scene(layout); 
     stage.setScene(scene); 
     stage.show(); 
    } 

    private static final int NUM_LABELS = 20; 
    public static void main(String[] args) { Application.launch(args); } 
} 

자바 FX 8은 당신이 원하는 경우, 당신은 CSS를하지 않고 같은 일을 수행 할 수 있도록, 자바 API를 사용하여 지역의 배경을 설정할 수 있습니다. 라벨의 일부와

샘플 프로그램의 출력은 클릭과 점멸의 다양한 상태 : clicktoflash