Přidat otázku mezi oblíbenéZasílat nové odpovědi e-mailemVyřešeno Java - Jak projít v Timeline celé pole?

Dobrý den,

Potřebuji upravit tento kód

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Example extends javafx.application.Application {
    
    int index = 0;

    @Override
    public void start(Stage primaryStage) {
        var rectangle = new Rectangle(200, 200, Color.BLACK);
        var list_of_color = new Color[]{Color.PINK, Color.MAGENTA, Color.RED, Color.GREEN, Color.YELLOW, Color.TURQUOISE, Color.BLUE, Color.ORANGE};
        rectangle.setOnMouseClicked((t) -> {
            var timeline = new Timeline(new KeyFrame(Duration.millis(400), (event) -> {
                rectangle.setFill(list_of_color[index]);
                index += 1;
            }));
            timeline.setCycleCount(list_of_color.length);
            timeline.play();
        });
        primaryStage.setTitle("Example!");
        primaryStage.setScene(new Scene(new Pane(rectangle)));
        primaryStage.show();
    }

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

Jde o to, že musím vytvořit řídící proměnou, abych mohl v timeline projít postupně všechny prvky pole.
A problémem je, že v lambda výrazu nelze používat local variable (local variable referenced from a lambda expression must be final or effectively final), proto jsem musel použít global variable.

Nevíte, prosím Vás, jak to upravit, abych globální proměnou nemusel použít?

Děkuji

Předmět Autor Datum
Ahoj Jde o to, že musím vytvořit řídící proměnou, abych mohl v timeline projít postupně všechny prv…
Flash_Gordon 21.04.2020 23:39
Flash_Gordon
Promiňte, ale Váš kód už z principu nefunguje. Timeline pokaždé provede celý cyklus, takže barva je…
MichalDM 22.04.2020 23:41
MichalDM
Otázka je, čo vlastne chceš dosiahnuť. S JavaFX nemám skúsenosti, ale ak sa má po každom kliknutí sp…
moose 22.04.2020 07:33
moose
Napadlo mě nevytvářet Rectangle, ale objekt vlastní třídy extend Rectangle, který by měl navíc field…
MichalDM 22.04.2020 23:37
MichalDM
Po prečítaní dokumentácie to vyzerá tak, že prvý parameter KeyFrame je čas, v ktorom má daná zmena n…
moose 23.04.2020 00:20
moose
Ano, vím o FillTransition. Šlo mi pouze o znázornění mého problému. Každopádně perfektní. Už to fung… poslední
MichalDM 23.04.2020 23:18
MichalDM

Ahoj

Jde o to, že musím vytvořit řídící proměnou, abych mohl v timeline projít postupně všechny prvky pole.

[Jen tak plácám]

Java není úplně můj šálek kávy, ani jsem příklad příliš neprocházel do detailu.
Možná napíši hloupost, ale proč to pole jednoduše neproiteruješ?

Něco jako:


@Override
    public void start(Stage primaryStage) {
        var rectangle = new Rectangle(200, 200, Color.BLACK);
        var list_of_color = new Color[]{Color.PINK, Color.MAGENTA, Color.RED, Color.GREEN, Color.YELLOW, Color.TURQUOISE, Color.BLUE, Color.ORANGE};
        rectangle.setOnMouseClicked((t) -> {
            var timeline = new Timeline(new KeyFrame(Duration.millis(400), (event) -> {
                
                         for (Color color : list_of_color) {
                               rectangle.setFill(color);
                              }
                 
             }));
            timeline.setCycleCount(list_of_color.length);
            timeline.play();
        });
        primaryStage.setTitle("Example!");
        primaryStage.setScene(new Scene(new Pane(rectangle)));
        primaryStage.show();
    }

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

Pokud je nežádoucí, že prohledávání začne vždycky od prvního prvku, můžeš si odvodit vlastní třídu Color, která bude mít navíc pro tuto příležitost property
enum used -> true, false.
A to zohledníš při podmínce při použití.

[/Jen tak plácám]

Otázka je, čo vlastne chceš dosiahnuť. S JavaFX nemám skúsenosti, ale ak sa má po každom kliknutí spustiť animácia s nasledujúcou farbou, tak si stav niekde musíš pamätať. To, čo máš, by mohlo fungovať. Inak ak chceš meniť stav v lambde, tak môžeš použiť wrapper, ktorý bude final, ako napr. AtomicInteger.

Ak chceš na jedno kliknutie prejsť všetky farby, tak v tom prípade to bude vyzerať nejako takto:

rectangle.setOnMouseClicked(mouseEvent -> {
    final var timeline = new Timeline();
    Arrays.stream(COLORS)
            .map(color -> new KeyFrame(Duration.millis(400), event -> { rectangle.setFill(color); }))
            .forEach(timeline.getKeyFrames()::add);
    timeline.setCycleCount(COLORS.length);
    timeline.play();
});

Napadlo mě nevytvářet Rectangle, ale objekt vlastní třídy extend Rectangle, který by měl navíc field int. A s tím bych pracoval. Nicméně, lepší je použít AtomicInteger.

Děkuji. Nejslibněji vypadá Váš kód. Bohužel nefunguje (Rectangle se změní rovnou na poslední barvu v poli) a nepřišel jsem na to, jak to opravit. V čem je, prosím Vás, problém?

Po prečítaní dokumentácie to vyzerá tak, že prvý parameter KeyFrame je čas, v ktorom má daná zmena nastať (pôvodne som myslel, že je to doba trvania frame-u). Takže ju treba vypočítať podľa indexu, asi takto:

IntStream.range(0, COLORS.length)
        .mapToObj(index -> new KeyFrame(Duration.millis(index * 400), event -> { rectangle.setFill(COLORS[index]); }))
        .forEach(timeline.getKeyFrames()::add);

Ak má byť farebný prechod plynulý, tak by som si pozrel dokumentáciu k FillTransition.

Ano, vím o FillTransition. Šlo mi pouze o znázornění mého problému. Každopádně perfektní. Už to funguje. Děkuji

Výsledný kód teda vypadá:

import java.util.stream.IntStream;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;

public class Example extends javafx.application.Application {

    @Override
    public void start(Stage primaryStage) {
        var rectangle = new Rectangle(200, 200, Color.BLACK);
        var list_of_color = new Color[]{Color.PINK, Color.MAGENTA, Color.RED, Color.GREEN, Color.YELLOW, Color.TURQUOISE, Color.BLUE, Color.ORANGE};
        var timeline = new Timeline();
        rectangle.setOnMouseClicked((t) -> {
            IntStream
                    .range(0, list_of_color.length)
                    .mapToObj(
                            index -> new KeyFrame(
                              Duration.millis(index * 400), event -> {
                                rectangle.setFill(list_of_color[index]);
                              }
                            )
                    )
                    .forEach(timeline.getKeyFrames()::add);
            timeline.play();
        });
        primaryStage.setTitle("Example!");
        primaryStage.setScene(new Scene(new Pane(rectangle)));
        primaryStage.show();
    }

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

Zpět do poradny Odpovědět na původní otázku Nahoru