Animate node into layout - java

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:
Add the image to the TilePane layout.
Get the images position.
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?

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:
Fish in a tiled school after animation:
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);
}
}

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();
}

Related

how to dynamically change values for a sequence of animations?

I'm animating some pictures. Animations of each of them are performed sequentially. The pictures are in the hbox, the coordinates of which I track through layoutX. At
the end of each animation, I do a check. If the check is successful, then I delete some pictures. Moreover, the coordinates of others are shifted. Since transition was originally set in a loop (before all animations), animations (and recalculation of x) occur during animations, the result is bad.
How can I solve this problem?
There are two hboxes (blue rectangles). They contain pictures (dark blue rectangles) that, when animated, beat a picture from another hbox. During the impact, some images may be deleted.
import javafx.animation.SequentialTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
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 Main extends Application {
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) {
stage.setTitle("Есока");
Pane pane = new Pane();
stage.setScene(new Scene(pane,350,300));
HBox h1 = new HBox();
HBox h2 = new HBox();
h2.setAlignment(Pos.CENTER);
h2.setLayoutY(150);
h2.setPrefWidth(330);
pane.getChildren().addAll(h1, h2);
Rectangle r1 = new Rectangle(100, 100, Color.BLUE);
Rectangle r2 = new Rectangle(100, 100, Color.RED);
Rectangle r3 = new Rectangle(100, 100, Color.GREEN);
Rectangle r4 = new Rectangle(100, 100, Color.BLACK);
Rectangle r5 = new Rectangle(100, 100, Color.BROWN);
h1.setSpacing(10);
h2.setSpacing(10);
h1.getChildren().addAll(r1, r2, r3);
h2.getChildren().addAll(r4, r5);
pane.layout();
SequentialTransition st=new SequentialTransition();
for (int i = 0; i < 5; i++) {
int j1= (int) (Math.random()*h1.getChildren().size());
int j2= (int) (Math.random()*h2.getChildren().size());
TranslateTransition translateTransition=new TranslateTransition();
double x=h2.getChildren().get(j2).getLayoutX()-
h1.getChildren().get(j1).getLayoutX();
translateTransition.setByX(x);
double y=150;
translateTransition.setByY(y);
translateTransition.setAutoReverse(true);
translateTransition.setCycleCount(2);
translateTransition.setNode(h1.getChildren().get(j1));
translateTransition.setOnFinished(actionEvent -> {
if(Math.random()>0.5)h2.getChildren().remove(j2);
});
st.getChildren().add(translateTransition);
if(h1.getChildren().size()<=0|h2.getChildren().size()<=0)break;
}
st.setDelay(Duration.seconds(1));
st.play();
stage.show();
}
}
In this code, x is considered in a loop, and some images are deleted after, which is why the animation is incorrect. The problem is visible when the first object is removed (the "blow" trace goes to false coordinates).
It's not at all clear what you're trying to do, but you probably want to compute the next transition when the previous one finishes, instead of trying to compute them all at once.
public class Main extends Application {
private final Random rng = new Random();
private Pane pane;
private HBox h1;
private HBox h2;
public static void main(String[] args) {
launch();
}
#Override
public void start(Stage stage) {
stage.setTitle("Есока");
pane = new Pane();
stage.setScene(new Scene(pane,350,300));
h1 = new HBox();
h2 = new HBox();
h2.setAlignment(Pos.CENTER);
h2.setLayoutY(150);
h2.setPrefWidth(330);
pane.getChildren().addAll(h1, h2);
Rectangle r1 = new Rectangle(100, 100, Color.BLUE);
Rectangle r2 = new Rectangle(100, 100, Color.RED);
Rectangle r3 = new Rectangle(100, 100, Color.GREEN);
Rectangle r4 = new Rectangle(100, 100, Color.BLACK);
Rectangle r5 = new Rectangle(100, 100, Color.BROWN);
h1.setSpacing(10);
h2.setSpacing(10);
h1.getChildren().addAll(r1, r2, r3);
h2.getChildren().addAll(r4, r5);
doAnimation();
stage.show();
}
private void doAnimation() {
pane.layout();
int j1= rng.nextInt(h1.getChildren().size());
int j2= rng.nextInt(h2.getChildren().size());
TranslateTransition translateTransition=new TranslateTransition();
double x=h2.getChildren().get(j2).getLayoutX()-
h1.getChildren().get(j1).getLayoutX();
translateTransition.setByX(x);
double y=150;
translateTransition.setByY(y);
translateTransition.setAutoReverse(true);
translateTransition.setCycleCount(2);
translateTransition.setNode(h1.getChildren().get(j1));
translateTransition.setOnFinished(actionEvent -> {
if(rng.nextBoolean()) {
h2.getChildren().remove(j2);
}
if (h2.getChildren().size() > 0) {
doAnimation();
}
});
translateTransition.play();
}
}

Line painting in painting program JavaFX

I created a simple paint program which allows the user to choose between 4 shapes(line,circle,rectangle,ellipse) , the user can change the width height and stroke width , he can save the design he makes , undo and redo , the user can also choose the stroke type (solid or dashed) ,I'm almost done with the program but I'm facing one problem , the line is getting displayed not in the way I want it to be displayed as the photo below :
So the line here is of width 32 and height 32 , my line is getting printed in a different way.Is it correct the way I'm printing my line ?
NOTE : I'm using the line shape in the code to make the code small.
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.*;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
public class paintLine extends Application {
#Override
public void start(Stage primaryStage) {
Image image1 = new
Image("C:\\Users\\Mhamd\\Desktop\\laol\\src\\resources\\Daco_70400.png",
100, 100, false, false);
ImageView view1 = new ImageView(image1);
view1.setFitHeight(40);
view1.setPreserveRatio(true);
ToggleButton linebtn = new ToggleButton();
linebtn.setGraphic(view1);
ToggleButton[] toolsArr = {linebtn};
ToggleGroup tools = new ToggleGroup();
for (ToggleButton tool : toolsArr) {
tool.setMinWidth(50);
tool.setToggleGroup(tools);
tool.setCursor(Cursor.HAND);
}
ColorPicker cpLine = new ColorPicker(Color.BLACK);
ColorPicker cpFill = new ColorPicker(Color.TRANSPARENT);
TextField textWidth = new TextField("32");
TextField textHeight = new TextField("32");
TextField contouring = new TextField("2");
Label line_color = new Label("Line Color");
Label fill_color = new Label("Fill Color");
Label line_width = new Label("3.0");
Label imgWidth = new Label("Width");
Label imgHeight = new Label("Height");
String week_days[] =
{"Solid", "Dotted"};
ChoiceBox choiceBox = new ChoiceBox(FXCollections
.observableArrayList(week_days));
VBox btns = new VBox(10);
btns.getChildren().addAll(linebtn, imgWidth, textWidth, imgHeight,
textHeight, line_color, cpLine,
fill_color, cpFill, line_width, contouring, choiceBox);
btns.setPadding(new Insets(5));
btns.setStyle("-fx-background-color: #999");
btns.setPrefWidth(100);
Canvas canvas = new Canvas(1080, 790);
GraphicsContext gc;
gc = canvas.getGraphicsContext2D();
gc.setLineWidth(1);
Line line = new Line();
canvas.setOnMouseClicked(e -> {
if (linebtn.isSelected()) {
// double widthSize =
Double.parseDouble(textWidth.getText());
// double heightSize =
Double.parseDouble(textHeight.getText());
double strokeWidth =
Double.parseDouble(contouring.getText());
// gc.setLineWidth(strokeWidth);
gc.setStroke(cpLine.getValue());
if (choiceBox.getSelectionModel().isSelected(0)) {
gc.setLineWidth(strokeWidth);
} else if (choiceBox.getSelectionModel().isSelected(1)) {
gc.setLineWidth(strokeWidth);
gc.setLineDashes(10);
}
gc.setFill(cpFill.getValue());
line.setStartX(e.getX());
line.setStartY(e.getY());
line.setEndX(e.getX() / 2);
line.setEndY(e.getY() / 2);
gc.strokeLine(line.getStartX(), line.getStartY(),
line.getEndX(), line.getEndY());
}
});
BorderPane pane = new BorderPane();
pane.setRight(btns);
pane.setCenter(canvas);
Scene scene = new Scene(pane, 1200, 800);
primaryStage.setTitle("Paint");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Focusing on just drawing a line interactively, you need to at least do the following:
Record the initial point at which the mouse was pressed and then
Render the line when you know where the mouse was released:
private double startX;
private double startY;
…
canvas.setOnMousePressed(e -> {
startX = e.getX();
startY = e.getY();
});
canvas.setOnMouseReleased(e -> {
gc.strokeLine(startX, startY, e.getX(), e.getY());
});
This works as shown above, but the problem then becomes how to draw the line while dragging—without damaging previous work. One solution is to add each new line to a List<Line> and render the accumulated lines with each update:
private final List<Line> lines = new ArrayList<>();
…
canvas.setOnMouseReleased(e -> {
lines.add(new Line(startX, startY, e.getX(), e.getY()));
});
canvas.setOnMouseDragged(e -> {
if (lineButton.isSelected()) {
…
gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
lines.forEach(l -> {
gc.strokeLine(l.getStartX(), l.getStartY(), l.getEndX(), l.getEndY());
});
gc.strokeLine(startX, startY, e.getX(), e.getY());
}
});
A more general solution, which illustrates undo() and redo(), is offered here.

How to update part of BufferedImage and display it with javafx

I have an image. At the beginning I want the image to be displayed with only the first frame. On the keyboard with 'u' key pressed, drink water and load the second frame. If I understand correctly the whole image should be loaded at the beginning. And then I need to set new y.
How to complete this task? What is the right way to display this image?
My code in:
package testDesktopUi;
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class DesktopUi extends Application {
private static int y = 0;
private static BufferedImage bufferedImage;
private static final ImageView imageView = new ImageView();
private static final HBox root = new HBox();
#Override
public void start(Stage primaryStage) throws IOException {
String path = "Bottle.png";
bufferedImage = ImageIO.read(new File(path)).getSubimage(0, 0, 32, 32);
Image image = SwingFXUtils.toFXImage(bufferedImage, null);
imageView.setImage(image);
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
updateImage(bufferedImage);
System.out.println(y);
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private static void updateImage(BufferedImage bufferedImage) {
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
//?? bufferedImage.getSubimage(0, y, 32, 32);
}
}
If you're using JavaFX, stick just to the JavaFX image API: there is no need to first load an AWT BufferedImage and convert it to a JavaFX image.
To display portions of an image, you can create an ImageView from the image and set the ImageView's viewport.
import javafx.application.Application;
import javafx.geometry.Rectangle2D;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class DesktopUi extends Application {
private int y = 0;
private final ImageView imageView = new ImageView();
private final HBox root = new HBox();
#Override
public void start(Stage primaryStage) {
String path = "Bottle.png";
// Assumes Bottle.png is in the same package as the current class:
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
// display only a portion of the image:
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
root.getChildren().add(imageView);
Scene scene = new Scene(root);
scene.setOnKeyPressed(key -> {
if (key.getCode() == KeyCode.U) {
System.out.println("u pressed");
System.out.println(y);
updateImage();
}
});
primaryStage.setScene(scene);
primaryStage.show();
}
private void updateImage() {
// update y
int maxHeight = 352;
if (y + 32 >= maxHeight) {
y = 0;
} else {
y += 32;
}
// update portion of image displayed
imageView.setViewport(new Rectangle2D(0, y, 32, 32));
}
public static void main(String[] args) {
Application.launch(args);
}
}
Note this also lends itself nicely to animations:
#Override
public void start(Stage primaryStage) {
int numSprites = 11 ;
Image image = new Image(getClass().getResource("Bottle.png").toExternalForm());
imageView.setImage(image);
IntegerProperty spriteIndex = new SimpleIntegerProperty();
spriteIndex.addListener((obs, oldIndex, newIndex) -> System.out.println(newIndex));
imageView.viewportProperty().bind(Bindings.createObjectBinding(
() -> new Rectangle2D(0, spriteIndex.get() * 32 , 32, 32),
spriteIndex
));
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(5),
new KeyValue(spriteIndex, numSprites - 1)));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
root.getChildren().add(imageView);
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}

Setting a node in a specific location Javafx [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I'm trying to make a Scrabble game, but I'm having trouble adding the tiles to match exactly to the board coordinates. SO far I'm having trouble figuring that out. Here is my code
import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Main extends Application {
public static final int TILE_SIZE = 40;
private static final int W = 800;
private static final int H = 600;
private static final int X_TILES = 15;
private static final int Y_TILES = 15;
private Tile[][] grid = new Tile[X_TILES][Y_TILES];
private GameTile[][] bag = new GameTile[19][19];
double orgSceneX, orgSceneY;
double orgTranslateX, orgTranslateY;
Group tileGroup = new Group();
Group pieceGroup = new Group();
private Scene scene;
static public void main(String[] args) {
launch(args);
}
#Override
public void start(Stage stage) throws Exception {
scene = new Scene(createContent());
stage.setScene(scene);
stage.show();
}
private Parent createContent() {
Pane root = new Pane();
root.setPrefSize(W, H);
root.getChildren().addAll(tileGroup, pieceGroup);
//creates board tiles
for (int x = 0; x < X_TILES; x++) {
for (int y = 0; y < Y_TILES; y++) {
Tile tile = new Tile(x, y);
grid[y][x] = tile;
tileGroup.getChildren().add(tile);
}
}
int z = 0;
//creates bag tiles
for (int y = 0; y < 3; y++) {
for (int x = 15; x < 18; x++) {
GameTile bagTile = new GameTile(x, y);
bagTile.setCursor(Cursor.HAND);
bagTile.setOnMousePressed(e -> {
orgSceneX = e.getSceneX();
orgSceneY = e.getSceneY();
orgTranslateX = ((GameTile) (e.getSource())).getTranslateX();
orgTranslateY = ((GameTile) (e.getSource())).getTranslateY();
});
bagTile.setOnMouseDragged(e -> {
double offsetX = e.getSceneX() - orgSceneX;
double offsetY = e.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
((GameTile) (e.getSource())).setTranslateX((int)(newTranslateX));
((GameTile) (e.getSource())).setTranslateY((int)newTranslateY);
});
bagTile.setOnMouseReleased(e -> {
});
bag[x][y] = bagTile;
root.getChildren().add(bagTile);
z++;
}
}
return root;
}
}
I tried implementing a mouse handler, but so far I keep failing to understand how to make the exact coordinates match the board underneath the draggable tiles.
One way to do this is to use ideas from this answer. I altered the code so that it sets the node that is being dragged invisible and shows a snapshot of that node being dragged by the mouse.
You could probably use Label instead of Button for the Tiles.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.control.Button;
import javafx.scene.input.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
/**
* Demonstrates a drag-and-drop feature.
*/
public class App extends Application
{
#Override
public void start(Stage stage)
{
//Source Buttons.
final Button boat1 = new Button("boat1");
final Button boat2 = new Button("boat2");
final Button boat3 = new Button("boat3");
final Button boat4 = new Button("boat4");
//Adding OnDragDetected to source Buttons.
setOnDragDetected(boat1);
setOnDragDetected(boat2);
setOnDragDetected(boat3);
setOnDragDetected(boat4);
//Adding onDragDone to source Buttons.
setOnDragDone(boat1);
setOnDragDone(boat2);
setOnDragDone(boat3);
setOnDragDone(boat4);
//Creating GridPane
GridPane gridPane = new GridPane();
gridPane.setVgap(5);
gridPane.setHgap(5);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setStyle("-fx-background-color: black;");
//Adding StackPane to every Cell in the GridPane and Adding the Target Events to each StackPane.
for (int i = 0; i < 6; i++) {
StackPane stackPane = new StackPane();
stackPane.setPrefSize(150, 50);
stackPane.setStyle("-fx-background-color: yellow;");
setOnDragOver(stackPane);
setOnDragEntered(stackPane);
setOnDragExited(stackPane);
setOnDragDropped(stackPane);
gridPane.add(stackPane, i / 3, i % 3);
}
HBox root = new HBox(new VBox(boat1, boat2, boat3, boat4), gridPane);
stage.setTitle("Hello Drag And Drop");
Scene scene = new Scene(root, 400, 200);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
//source events handlers
public void setOnDragDetected(Button source)
{
source.setOnDragDetected((MouseEvent event) -> {
/* drag was detected, start drag-and-drop gesture*/
System.out.println("onDragDetected");
/* allow any transfer mode */
Dragboard db = source.startDragAndDrop(TransferMode.ANY);
db.setDragView(source.snapshot(new SnapshotParameters(), null));
source.setVisible(false);
/* put a string on dragboard */
ClipboardContent content = new ClipboardContent();
content.putString(source.getText());
db.setContent(content);
event.consume();
});
}
public void setOnDragDone(Button source)
{
source.setOnDragDone((DragEvent event) -> {
/* the drag-and-drop gesture ended */
System.out.println("onDragDone");
/* if the data was successfully moved, clear it */
// if (event.getTransferMode() == TransferMode.MOVE) {
// source.setText("");
// }
event.consume();
});
}
//target event handlers
public void setOnDragOver(StackPane target)
{
target.setOnDragOver((DragEvent event) -> {
/* data is dragged over the target */
System.out.println("onDragOver");
/* accept it only if it is not dragged from the same node
* and if it has a string data */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
/* allow for both copying and moving, whatever user chooses */
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
event.consume();
});
}
public void setOnDragEntered(StackPane target)
{
target.setOnDragEntered((DragEvent event) -> {
/* the drag-and-drop gesture entered the target */
System.out.println("onDragEntered");
/* show to the user that it is an actual gesture target */
if (event.getGestureSource() != target
&& event.getDragboard().hasString()) {
target.setStyle("-fx-background-color: green;");
}
event.consume();
});
}
public void setOnDragExited(StackPane target)
{
target.setOnDragExited((DragEvent event) -> {
/* mouse moved away, remove the graphical cues */
target.setStyle("-fx-background-color: yellow;");
event.consume();
});
}
public void setOnDragDropped(StackPane target)
{
target.setOnDragDropped((DragEvent event) -> {
/* data dropped */
System.out.println("onDragDropped");
/* if there is a string data on dragboard, read it and use it */
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasString()) {
//target.setText(db.getString());
Button tempBoat = new Button(db.getString());
tempBoat.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
target.getChildren().clear();
target.getChildren().add(tempBoat);
success = true;
}
/* let the source know whether the string was successfully
* transferred and used */
event.setDropCompleted(success);
event.consume();
});
}
}

Implementing drawing areas in tabs in JavaFX

I would want to add a drawing area to my tabs in JavaFX. I am new to the field, and tried adding a Canvas to each tab I create. However, this does not seem to be working. Relevant code included.
#FXML
void fileNewTabHandler(ActionEvent event) {
++indexTab;
Tab tab = new Tab("Untitled " + indexTab);
Canvas canvas = new Canvas(500, 285);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLUE);
gc.fillRect(250, 856, 50, 60);
tab.setContent(canvas);
tabPane.getTabs().add(tab);
}
Here tabPane and indexTab are defined and work as long as I do not use the canvas. Where am I going wrong. Should I use a different method to implement what I want to?
Quick Sample
There is no real trick to this, you just set the canvas as the content of the tab.
import javafx.application.Application;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import java.util.Random;
public class TabbedCanvas extends Application {
private int tabId = 0;
private double W = 200, H = 150;
private Random random = new Random(42);
#Override
public void start(Stage stage) {
TabPane tabPane = new TabPane();
Button newTabButton = new Button("New Tab");
newTabButton.setOnAction(
event -> addTab(tabPane)
);
newTabButton.fire();
ToolBar toolBar = new ToolBar(newTabButton);
toolBar.setMinHeight(ToolBar.USE_PREF_SIZE);
VBox layout = new VBox(toolBar, tabPane);
VBox.setVgrow(tabPane, Priority.ALWAYS);
stage.setScene(new Scene(layout));
stage.show();
}
private void addTab(TabPane tabPane) {
Tab tab = new Tab("Tab: " + tabId++);
tab.setContent(createTabContent());
tabPane.getTabs().add(tab);
tabPane.getSelectionModel().select(tab);
}
private Node createTabContent() {
Canvas canvas = new Canvas(W, H);
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(randomColor());
gc.fillRect(0, 0, W, H);
return canvas;
}
private Color randomColor() {
return Color.rgb(
random.nextInt(256),
random.nextInt(256),
random.nextInt(256)
);
}
public static void main(String[] args) {
launch(args);
}
}

Categories

Resources