I have ArrayList<ImageView> imageViews = new ArrayList<>(); and so it contains images.
Images are created on button click, saved to that array and appears on scene from that array. Code is somewhat messy, but hey, #makeItWork at first #makeItRight later...
public void btnAddAndAction() throws FileNotFoundException {
algorithm.addElement(algorithm.calculateId(),"AND",null);
imageViews.add(new ImageView(algorithm.getLastElementImg()));
imageViews.get(imageViews.size()-1).setX(25); //default pos
imageViews.get(imageViews.size()-1).setY(85);
pane.getChildren().add( imageViews.get(imageViews.size()-1));
pane.requestLayout();
imageViews.get(imageViews.size()-1).setOnMousePressed(imgOnMousePressedEventHandler);
imageViews.get(imageViews.size()-1).setOnMouseDragged(imgOnMouseDraggedEventHandler);
}
//FIXME
EventHandler<MouseEvent> imgOnMousePressedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
orgSceneX = t.getSceneX();
orgSceneY = t.getSceneY();
orgTranslateX = ((imageViews.get(imageViews.size()-1)).getTranslateX());
orgTranslateY = ((imageViews.get(imageViews.size()-1)).getTranslateY());
}
};
//FIXME
EventHandler<MouseEvent> imgOnMouseDraggedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
double offsetX = t.getSceneX() - orgSceneX;
double offsetY = t.getSceneY() - orgSceneY;
double newTranslateX = orgTranslateX + offsetX;
double newTranslateY = orgTranslateY + offsetY;
(imageViews.get(imageViews.size()-1)).setTranslateX(newTranslateX);
(imageViews.get(imageViews.size()-1)).setTranslateY(newTranslateY);
}
};
I'm trying to do drag and drop feature to the images and I am struggling to detect what's under mouse...
I've fount getSource from MouseEvent, but it needs object...
so: how do I know, which element of array is pressed so I could drag it??
Java 1.8, JavaFX, IntelliJ.
Solution is to CAST ImageView... Answer is in the comment section.
Related
I am trying to implement a full press-drag-release gesture with JavaFX. I want to drag a rectangle from one VBox to another. On the MOUSE_DRAG_RELEASED event that happens on the target VBox, I'm trying to add the dragged rectangle as a child of the target VBox.
The problem is that when I release the mouse on the target VBox, the rectangle does not get into the expected position inside the VBox, but is always offset to the right by a fixed distance.
public class DragFromOneVBoxToAnother extends Application {
private Disk sourceDisk = new Disk();
private VBox targetVBox = new VBox();
public static void main(String[] args) {
Application.launch(args);
}
#Override
public void start(Stage stage) {
// Build the UI
GridPane root = getUI();
// Add the event handlers
this.addEventHandlers();
Scene scene = new Scene(root, 800, 600);
stage.setScene(scene);
stage.show();
}
private GridPane getUI() {
GridPane pane = new GridPane();
VBox sourceVBox = new VBox();
sourceDisk.setWidth(90);
sourceDisk.setHeight(20);
sourceVBox.setStyle(" -fx-border-color:red; -fx-border-width: 1; -fx-border-style: solid;");
targetVBox.setStyle(" -fx-border-color:green; -fx-border-width: 1; -fx-border-style: solid;");
sourceVBox.getChildren().add(sourceDisk);
targetVBox.getChildren().add(new Rectangle(200, 20));
pane.setHgap(200);
pane.addColumn(0, sourceVBox);
pane.addColumn(1, targetVBox);
pane.setPadding(new Insets(200, 100, 200, 100));
return pane;
}
private void addEventHandlers() {
sourceDisk.setOnMouseEntered(event -> sourceDisk.setCursor(Cursor.HAND));
sourceDisk.setOnMousePressed(event -> {
sourceDisk.setOrgSceneX(event.getSceneX());
sourceDisk.setOrgSceneY(event.getSceneY());
sourceDisk.setOrgTranslateX(sourceDisk.getTranslateX());
sourceDisk.setOrgTranslateY(sourceDisk.getTranslateY());
sourceDisk.setMouseTransparent(true);
sourceDisk.setCursor(Cursor.CLOSED_HAND);
});
sourceDisk.setOnDragDetected(event -> sourceDisk.startFullDrag());
sourceDisk.setOnMouseDragged(event -> {
double offsetX = event.getSceneX() - sourceDisk.getOrgSceneX();
double offsetY = event.getSceneY() - sourceDisk.getOrgSceneY();
double newTranslateX = sourceDisk.getOrgTranslateX() + offsetX;
double newTranslateY = sourceDisk.getOrgTranslateY() + offsetY;
sourceDisk.setTranslateX(newTranslateX);
sourceDisk.setTranslateY(newTranslateY);
});
sourceDisk.setOnMouseReleased(event -> {
sourceDisk.setMouseTransparent(false);
sourceDisk.setCursor(Cursor.DEFAULT);
});
targetVBox.setOnMouseDragReleased(event ->
targetVBox.getChildren().add(sourceDisk));
}
private class Disk extends Rectangle {
private double orgSceneX;
private double orgSceneY;
private double orgTranslateX;
private double orgTranslateY;
// below, the getters and setters for all the instance variables
// were removed for brevity
}
I have found that, even though the visual representation of the dragged rectangle seems to be offset when it's dropped, a child appears to actually be added to the target VBox (this can be seen because the border of the VBox expands after the MOUSE_DRAG_RELEASED event).
What could be the issue?
During the mouse drag gesture you modify the translateX/translateY properties of the node. This results in the dragged node being offset from the position where the new parent places it by this transformation. You need to reset those values to properly add the node to the bottom of the VBox:
targetVBox.setOnMouseDragReleased(event -> {
targetVBox.getChildren().add(sourceDisk);
// reset translate values
sourceDisk.setTranslateX(0);
sourceDisk.setTranslateY(0);
});
I'm building a paint program using JavaFX. I'm trying to draw a Rectangle using GraphicsContext and have managed to get it to draw on the canvas. However, no matter where I click, it always starts from the origin. The rectangle stops being drawn when I release the mouse (this is done correctly) but isn't drawn from when I press down on the mouse. Here is my code:
public RectController(Canvas c, Scene s, BorderPane borderPane){
this.borderPane = borderPane;
this.scene = scene;
this.graphicsContext = canvas.getGraphicsContext2D();
rectangle = new Rectangle();
rectangle.setStrokeWidth(1.0);
rectangle.setManaged(true);
rectangle.setMouseTransparent(true);
//when you first press down on the mouse
pressMouse = event -> {
// borderPane.getChildren().add(rectangle); --> REMOVED
double startX = event.getX();
double startY = event.getY();
};
releaseMouse = event -> {
borderPane.getChildren().remove(rectangle);
double width = Math.abs(event.getX() - startX);
double height = Math.abs(event.getY() - startY);
graphicsContext.strokeRect(startX, startY, width, height);
borderPane.setOnMousePressed(null);
borderPane.setOnMouseDragged(null);
borderPane.setOnMouseReleased(null);
};
dragMouse = event -> {
rectangle.setWidth(event.getX());
rectangle.setHeight(event.getY());
};
}
Any help would be much appreciated.
Is this close to what you are asking?
#FXML AnchorPane apMain;
Rectangle rectangle;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML void handleOnMouseClick(MouseEvent event)
{
double x = event.getSceneX();
double y = event.getSceneY();
rectangle = new Rectangle();
rectangle.setX(x);
rectangle.setY(y);
rectangle.setWidth(25);
rectangle.setHeight(25);
apMain.getChildren().add(rectangle);
}
#FXML void handleOnMouseDrag(MouseEvent event)
{
rectangle.setWidth(event.getSceneX() - x);
rectangle.setHeight(event.getSceneY() - y);
}
#FXML void handleOnMouseRelease(MouseEvent event)
{
apMain.getChildren().remove(rectangle);
}
You will need to do work to this. It's crude.
I want to make an animation with the "width" of my node.
In this case my node is a "AnchorPane".
I try to make a navigation drawer in javafx.
there is no property "width Property ()"?
new Key Value (node.width Property (), 1, WEB_EASE)
node.widthProperty().getValue() not found
My code:
public void changeWidth(final Node node, double width) {
this.node = node;
this.timeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.millis(20),
new KeyValue( going here? , width, WEB_EASE)
)
)
.build();
setCycleDuration(Duration.seconds(5));
setDelay(Duration.seconds(0));
}
Example with "opacity" property:
new KeyValue(node.opacityProperty(), 1, WEB_EASE)
My class ConfigAnimationViewPane:
public class ConfigAnimationViewPane extends Transition {
protected static final Interpolator WEB_EASE = Interpolator.EASE_BOTH;
protected AnchorPane node;
protected Timeline timeline;
private boolean oldCache = false;
private CacheHint oldCacheHint = CacheHint.DEFAULT;
private final boolean useCache = true;
/**
* Called when the animation is starting
*/
protected void starting() {
if (useCache) {
oldCache = node.isCache();
oldCacheHint = node.getCacheHint();
node.setCache(true);
node.setCacheHint(CacheHint.SPEED);
}
}
/**
* Called when the animation is stopping
*/
protected void stopping() {
if (useCache) {
node.setCache(oldCache);
node.setCacheHint(oldCacheHint);
}
}
#Override protected void interpolate(double d) {
timeline.playFrom(Duration.seconds(d));
timeline.stop();
}
}
This is mi controller:
Move the menu to the left (the occult)
LeftTransition leftTransition = new LeftTransition();
leftTransition.OpenMenu(list1);
leftTransition.play();
Here I want to put my size "AnchorPane".
(Set the width of my "anchorpane")
/*ViewPaneTransition paneTransition = new ViewPaneTransition();
paneTransition.CloseMenu(viewPane, width );
paneTransition.play();*/
Here is a working example for java 9. It changes both the width and the height (just remove the height line if you don't need it)
The widthProperty is readOnly so yo have to set either maxWidth or minWidth switch the case you need.
the delay duration on timeline is 0 by default, no need to set it, and the cycleduration is computed from the keyframes durations.
public void changeSize(final Pane pane, double width, double height) {
Duration cycleDuration = Duration.millis(500);
Timeline timeline = new Timeline(
new KeyFrame(cycleDuration,
new KeyValue(pane.maxWidthProperty(),width,Interpolator.EASE_BOTH)),
new KeyFrame(cycleDuration,
new KeyValue(pane.maxHeightProperty(),height,Interpolator.EASE_BOTH))
);
timeline.play();
timeline.setOnFinished(event->{
/* insert code here if you need */
});
}
public void changeWidth(final Pane/*Region*/ node, double width) {//its a Pane or Regions
this.node = node;
this.timeline = TimelineBuilder.create()
.keyFrames(
new KeyFrame(Duration.millis(20),
new KeyValue( going here? , width, WEB_EASE)
)
)
.build();
setCycleDuration(Duration.seconds(5));
setDelay(Duration.seconds(0));
}
In this case my node is a "AnchorPane".
your AnchorPane is a subclass or Pane, let your wrappers or methods take in Pane or their respective class
guys! I am having an issue in my JavaFX Application. Images are able to be dragged and placed anywhere on the entire pane. This was working wonderfully, up until I added a second event handler. I added a second event handler to handle deletion. Right-clicking on the image gives you a pop up menu with deletion as an option. As soon as I added this, dragging stopped working properly. While it still works somewhat, the mouse is no longer ever on the image. As soon as you begin dragging, the image jumps over and down like 300 px? I am still able to drag, but I am not dragging on top of the image.
I understand this explanation isn't clear, and I can easily upload a gif of what happens if someone wants me to. Here's the code:
#FXML
private void handleImageButton()
{
System.out.println("Image Clicked");
closeMenus(6);
//contains the image path or image URL
String picURL = imageURL.getText();
if (!picURL.equals(""))
{
Image image = new Image(new File(picURL).toURI().toString());
ImageView iv = new ImageView();
iv.setImage(image);
iv.setCursor(Cursor.MOVE);
iv.setOnMousePressed(imageOnMousePressedEventHandler);
iv.setOnMouseDragged(imageOnMouseDraggedEventHandler);
workspace.getChildren().add(iv);
final ContextMenu contextMenu = new ContextMenu();
MenuItem delete = new MenuItem("Delete");
contextMenu.getItems().addAll(delete);
iv.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent e){
if (e.isSecondaryButtonDown()){
contextMenu.show(workspace, e.getScreenX(), e.getScreenY());
delete.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event){
workspace.getChildren().remove(iv);
}
});
} //end if
}
});
}
}
EventHandler<MouseEvent> imageOnMousePressedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (ImageTools.isVisible())
{
originalX = t.getSceneX();
originalY = t.getSceneY();
newX = ((ImageView)(t.getSource())).getTranslateX();
newY = ((ImageView)(t.getSource())).getTranslateY();
System.out.println("(" + newX + ", " + newY + ")");
}
}
};
EventHandler<MouseEvent> imageOnMouseDraggedEventHandler =
new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent t) {
if (ImageTools.isVisible())
{
double offsetX = t.getSceneX() - originalX;
double offsetY = t.getSceneY() - originalY;
double newTranslateX = newX + offsetX;
double newTranslateY = newY + offsetY;
((ImageView)(t.getSource())).setTranslateX(newTranslateX);
((ImageView)(t.getSource())).setTranslateY(newTranslateY);
System.out.println("(" + newTranslateX + ", " + newTranslateY + ")");
}
}
};
The problem is that you are overriding your mouse listeners:
iv.setOnMousePressed(imageOnMousePressedEventHandler);
iv.setOnMouseDragged(imageOnMouseDraggedEventHandler);
iv.setOnMousePressed(new EventHandler() {});
setOnXYZ is a setter for a property, holding only one listener, so you are effectively overwriting your listeners in steps 1 and 3.
Use Node.addEventHandler() to add multiple handlers for the same EventType or in this case i would suggest using setOnContextMenuRequested to show the context menu because it is more convenient and system independent.
I am creating an application in JavaFx, In which I want to do that if any child stage is getting opened then it should be opened in center of parent stage.
I am trying to do this using mystage.centerOnScreen() but it'll assign the child stage to center of screen, not the center of parent stage.
How can I assign the child stage to center of parent stage?
private void show(Stage parentStage) {
mystage.initOwner(parentStage);
mystage.initModality(Modality.WINDOW_MODAL);
mystage.centerOnScreen();
mystage.initStyle(StageStyle.UTILITY);
mystage.show();
}
You can use the parent stage's X/Y/width/height properties to do that. Rather than using Stage#centerOnScreen, you could do the following:
public class CenterStage extends Application {
#Override
public void start(final Stage stage) throws Exception {
stage.setX(300);
stage.setWidth(800);
stage.setHeight(400);
stage.show();
final Stage childStage = new Stage();
childStage.setWidth(200);
childStage.setHeight(200);
childStage.setX(stage.getX() + stage.getWidth() / 2 - childStage.getWidth() / 2);
childStage.setY(stage.getY() + stage.getHeight() / 2 - childStage.getHeight() / 2);
childStage.show();
}
public static void main(String[] args) {
Application.launch(args);
}
}
When you don't determine a size for the childStage, you have to listen for width and height changes as width and height is still NaN when onShown is called.
final double midX = (parentStage.getX() + parentStage.getWidth()) / 2;
final double midY = (parentStage.getY() + parentStage.getHeight()) / 2;
xResized = false;
yResized = false;
newStage.widthProperty().addListener((observable, oldValue, newValue) -> {
if (!xResized && newValue.intValue() > 1) {
newStage.setX(midX - newValue.intValue() / 2);
xResized = true;
}
});
newStage.heightProperty().addListener((observable, oldValue, newValue) -> {
if (!yResized && newValue.intValue() > 1) {
newStage.setY(midY - newValue.intValue() / 2);
yResized = true;
}
});
newStage.show();