0

I have an ImageView which I add to a TilesPane layout. I'd like to animate the ImageView from a specified (x,y) position to the position it would be added to.

I tried something like this:

TilePane tp = new TilePane();

...

ImageView iv = new ImageView(img);
tp.getChildren().add(iv);

TranslateTransition tt = new TranslateTransition(Duration.seconds(5), iv);
tt.setFromX(0);
tt.setFromY(0);
tt.setToX(iv.x);
tt.setToY(iv.y);
tt.play();

But I get this error: error: x has private access in ImageView

What I tried to do:

  1. Add the image to the TilePane layout.
  2. Get the images position.
  3. Animate the image from a specified (x,y) position to its current position.

If this isn't the correct/best way to do this, what are the alternatives? If it is, how can I get around this error?

olfek
  • 3,210
  • 4
  • 33
  • 49
  • Please show [all of the relevant code](https://stackoverflow.com/help/mcve) and a complete stack trace (if there is one). You do not say how you do "2. Get the images position." – jewelsea Apr 21 '17 at 17:31
  • @jewelsea Using `iv.x` and `ix.y` for the x and y coordinates respectively – olfek Apr 21 '17 at 17:32
  • Oh I see, that would not compile. You need to [generate a layout pass](http://stackoverflow.com/questions/26152642/get-the-height-of-a-node-in-javafx-generate-a-layout-pass) to determine where the node will end up when its layoutX and layoutY values are set, then setFrom to the negative difference of the source location to the layout location. You don't specify what the co-ordinate system of the source location is. Perhaps if you use scene co-ordinates and sceneToLocal. Just some thoughts, maybe I'll give it a try. – jewelsea Apr 21 '17 at 17:37
  • For the immediate compile error, there are many, many questions addressing that. (Just try a [search](http://stackoverflow.com/search?q=%5Bjava%5D+error+private+access).) [This one](http://stackoverflow.com/questions/20392007/error-due-to-private-access), for example, describes the solution. Note, as others have pointed out, there are a lot of other issues (`x` and `y` are not the correct properties to begin with - they have nothing to do with the `ImageView`'s location, the `TranslateTransition` manipulates the translation, not the layout, the image view has not been laid out yet, etc etc). – James_D Apr 21 '17 at 17:42

2 Answers2

2

This is what I came up with, it is similar to Fabian's answer, so doesn't add much. The starting points are calculated at random and are in scene co-ordinates, then converted to local co-ordinates via the sceneToLocal function.

For some reason (which I don't understand) the layout pass request on the tilePane did not seem to work until after a stage was shown (before the stage was shown the tilePane height and width would show as 0). So I show the stage first before trying to do any calculations and animation.

Fish at random starting positions:

randomstart

Fish in a tiled school after animation:

ordered finish

import javafx.animation.*;
import javafx.application.Application;
import javafx.geometry.*;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.*;
import java.util.stream.Collectors;

public class Layup extends Application {

    private final Random random = new Random(42);

    // image license: linkware - backlink to http://www.fasticon.com
    private static final String[] IMAGE_LOCS = {
            "http://icons.iconarchive.com/icons/fasticon/fish-toys/128/Blue-Fish-icon.png",
            "http://icons.iconarchive.com/icons/fasticon/fish-toys/128/Red-Fish-icon.png",
            "http://icons.iconarchive.com/icons/fasticon/fish-toys/128/Yellow-Fish-icon.png",
            "http://icons.iconarchive.com/icons/fasticon/fish-toys/128/Green-Fish-icon.png"
    };

    @Override
    public void start(Stage stage) throws Exception {
        TilePane tilePane = createTilePane(IMAGE_LOCS);

        Scene scene = new Scene(new Group(tilePane));

        stage.setScene(scene);
        stage.show();
        stage.setTitle("Click on the scene to animate");

        animateIn(tilePane);

        scene.setOnMouseClicked(event -> animateIn(tilePane));
    }

    private TilePane createTilePane(String[] imageLocs) {
        TilePane tilePane = new TilePane(10, 10);
        tilePane.setPrefColumns(2);

        Arrays.stream(imageLocs).map(
                loc -> new ImageView(new Image(
                        loc, 64, 0, true, true
                ))
        ).collect(Collectors.toCollection(tilePane::getChildren));

        tilePane.setPadding(new Insets(200));
        return tilePane;
    }

    private void animateIn(TilePane tilePane) {
        // layout tilePane
        tilePane.applyCss();
        tilePane.layout();

        double W = (int) tilePane.getScene().getWidth();
        double H = (int) tilePane.getScene().getHeight();

        ParallelTransition pt = new ParallelTransition();

        for (int i = 0; i < IMAGE_LOCS.length; i++) {
            Node child = tilePane.getChildren().get(i);

            Point2D start = new Point2D(
                    random.nextInt((int) (W - child.getBoundsInLocal().getWidth())),
                    random.nextInt((int) (H - child.getBoundsInLocal().getHeight()))
            );

            Point2D startInLocal = child.sceneToLocal(start);

            TranslateTransition tt = new TranslateTransition(Duration.seconds(.5), child);
            tt.setFromX(startInLocal.getX());
            tt.setFromY(startInLocal.getY());
            tt.setToX(0);
            tt.setToY(0);

            pt.getChildren().add(tt);
        }

        pt.play();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Bit of a late reply (sorry), I'd really like to accept your answer, it has one thing missing, how can we get the fish to start from the same position? – olfek Jun 07 '17 at 13:40
  • 1
    Just use a StackPane rather than a TilePane if you want everything to start from the same position. I just used a TilePane as that is what you use in your question, so I assumed you wanted the items to start from a tiled position. – jewelsea Jun 07 '17 at 19:14
1

The final position will be determined by the layoutX and layoutY properties after a layout pass. Furthermore any transition is relative to this position, so (0, 0) needs to be the end value of the transition.

Example

private static final double START_X = 50;
private static final double START_Y = 50;

@Override
public void start(Stage primaryStage) {
    TilePane tilePane = new TilePane();
    tilePane.setPrefSize(200, 200);
    Image image = new Image("https://cdn.sstatic.net/Sites/stackoverflow/company/img/logos/so/so-icon.png", 50, 50, true, false);
    Button btn = new Button("add");
    btn.setOnAction((ActionEvent event) -> {
        ImageView img = new ImageView(image);
        tilePane.getChildren().add(img);

        // layout tilePane
        tilePane.applyCss();
        tilePane.layout();

        // create transition from start position to final position
        TranslateTransition transition = new TranslateTransition(Duration.seconds(4), img);
        transition.setFromX(START_X - img.getLayoutX());
        transition.setFromY(START_Y - img.getLayoutY());
        transition.setToX(0);
        transition.setToY(0);

        transition.play();
    });

    VBox root = new VBox(btn, tilePane);

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}
fabian
  • 80,457
  • 12
  • 86
  • 114
  • Its not really the kind of thing I'm looking for, the images don't start from exactly the position specified, it needs start exactly at the coordinates in the scene. Its still relative to the image itself – olfek Apr 21 '17 at 18:20