1

私は JavaFX アプリケーションを持っています。その中には約 20 のラベルがあります。ユーザーがラベルをクリックすると、ラベルが赤く点滅する必要があります。ラベルを赤く点滅させるために、実際にはswing用に開発したスレッドをJavaFXに変換して使用しています。しばらくは機能していましたが、最近、アプリケーションのロックアップをラベルのアニメーションまでたどりました。私がアニメーションを作成した方法は単純でした:

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

tl はラベルの ArrayList を指し、idx はそのインデックスを指します。別のラベルにはクリック時イベントが関連付けられており、クリックすると、ラベルをアニメーション化する (点滅させる) スレッドが作成されます。

何らかの理由で、多くのラベルが押されている場合、アプリケーションがロックアップします。

JavaFXのスレッドセーフの問題だと確信しています。次のように、JavaFX アプリケーション スレッドを共有する別のスレッドがあります。

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

TimerUpdater は、点滅しているものであっても、ラベルのテキストを常に更新します。

ラベルアニメーターは次のとおりです。

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

2 に答える 2

4

JavaFXイベントキューがどのように機能するかについて少し誤解があると思います。

1)AnimateLabelコードを通常のスレッドで実行すると、Label#setStyle(...)コードがそのスレッドで実行されます。これは違法であり、問​​題が発生する可能性があります。

2)2番目の例のようにJavaFXイベントキューでAnimateLabelコードを完全に実行すると、アニメーションが完了するまでイベントスレッドがブロックされます。その間、アプリケーションは更新されず、ユーザーイベントを処理したり、再描画したりしません。基本的に、ループ内でラベルスタイルを変更しますが、イベントキューに実際にラベルを再描画する時間を与えていないため、画面に何も表示されません。

半正しいアプローチは、両方の混合です。別のスレッドでAnimateLabelを実行しますが、への呼び出しをLabel#setStyle(...)でラップしますPlatform#runLater(...)。このようにすると、関連する作業でイベントスレッドを煩わせるだけで、その間に他の作業(UIの更新など)を自由に行うことができます。

私が言ったように、これは半正しいアプローチです。なぜなら、あなたが望むことをより簡単に行うための組み込み機能があるからです。Transitionクラスを確認することをお勧めします。カスタムアニメーションのシンプルなアプローチを提供し、ノードの最も一般的なプロパティをアニメーション化するための事前に構築されたサブクラスの束も提供します。

于 2012-12-09T12:21:36.120 に答える
3

サルカンは素晴らしい答えを持っています。

この回答は、css スタイルを変更するためのタイムラインアプローチのサンプル コード ( zonski のフォーラム投稿に基づく) を提供するだけです。このコードは、質問に投稿したコードの代わりとして使用できます。

このアプローチの利点は、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); }
}

JavaFX 8 では、Java API を使用して領域の背景を設定できるため、必要に応じて CSS を使用せずに同じことを実行できます。

いくつかのラベルがクリックされ、さ​​まざまな状態で点滅しているプログラム出力の例: クリックフラッシュ

于 2012-12-10T01:42:29.723 に答える