Right now I'm trying to code a simple abacus with JavaFX. There are balls on horizontal rails arranged in a gridpane, and I'm using a translateTransition to move them to the right on click. The following is the code I have right now, which works fine on any of the balls in the grid except it only animates the movement to the right. On the second click, the ball jumps back to the left to its original position without the animation and I can't figure out why it's not animating. Any help or ideas would be greatly appreciated!
private void onClick(final Circle circle) {
circle.setOnMouseClicked(new EventHandler<MouseEvent>() {
public void handle(MouseEvent mouseEvent) {
//set movement direction, check if circle has been moved
int targetX = 200;
if (circle.getTranslateX() > 1) {
targetX = 0;
}
//animation trigger and details
TranslateTransition push = new TranslateTransition(Duration.millis(500));
push.setNode(circle);
push.setFromX(circle.getCenterX());
push.setToX(targetX);
push.play();
}
});
}
Related
I have been having this issue for a long time, and no matter what answers on here I try I keep having an issue.
This is a previous question of mine that had no resolution to it JavaFX 3D PerspectiveCamera affects drag position of a node
(nor did the answers/links provided in the question)
Essentially I am trying to drag a node while keeping the mouse position at to the clicked position of the node while dragging.
The original thought was to do an event.getScreenX() or event.getSceneX() to get the initial position on mouseClicked, and then compare/update in the mouse dragged.
The issue is that when I zoom the camera in and out(camera.setTranslateZ()), for some reason the values will increase/decrease depending on the zoom, i.e., the node drags slower/stays with the mouse when the camera is zoomed out.
For what it's worth I also have scaled the main node by 10, which I think might have something to do with this as one of the examples did seem to break when the scale and/or camera were changed; however the example also doesn't work, with no scale.
Does anyone have any idea? It's extremely frustrating with how simple this task is, yet hard to actually accomplish. I would think that as the mouse would drag, regardless if it dragged 1 pixel with the mouse zoomed in, or 100 pixels with the mouse zoomed out that it wouldn't cause this issue, so I'm wondering if there is some sort of bug with this? Any thoughts are appreciated, thank you.
public class Move extends Application {
double x0,xDiff;
double y0,yDiff;
#Override
public void start(Stage primaryStage) {
Box b = new Box(100,100,1);
b.setLayoutX(0);
b.setLayoutY(0);
// b.setTranslateZ(20000);
Pane root = new Pane();
root.getChildren().add(b);
PhongMaterial p = new PhongMaterial();
p.setDiffuseColor(Color.RED);
b.setMaterial(p);
Scene scene = new Scene(root, 2000, 1250,true);
PerspectiveCamera camera = new PerspectiveCamera(true);
camera.setTranslateZ(-1000);
camera.setFarClip(2000);
scene.setCamera(camera);
primaryStage.setTitle("Hello World!");
primaryStage.setScene(scene);
primaryStage.show();
b.setOnMousePressed(event
->{
x0 = event.getSceneX();
y0 = event.getSceneY();
event.consume();
});
b.setOnMouseDragged(event
->{
xDiff = event.getSceneX() - x0;
yDiff = event.getSceneY() - y0;
b.setLayoutX(b.getLayoutX() + xDiff);
b.setLayoutY(b.getLayoutY() + yDiff);
x0 = event.getSceneX();
y0 = event.getSceneY();
});
primaryStage.setOnScroll(event
->{
if (event.getDeltaY() > 0)
{
camera.setTranslateZ(camera.getTranslateZ() + 45);
}
else
{
camera.setTranslateZ(camera.getTranslateZ() - 45);
}
});
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
I'm developing an application in which I want to touch the screen vertically from top to bottom, and with the movement of finger I'm changing background.
I got that part of app.
The issue is that if a user touches the middle or the bottom part (coordinates) of the screen, It changes the background.
I want that user starts from top and move to bottom and if the user touches the middle or the bottom part (coordinates) of the screen, application shouldn't show any reaction but a toast should be shown saying
"Start touching from the top of the screen".
#Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int)event.getX();
int y = (int)event.getY();
if(MatchReservedPixels(x,y))
{
return false;
}
return true;
}
//and here is your MatchReservedPixels function
private boolean MatchReservedPixels(int x,int y)
{
//Here goes your pixel matching logic
}
and don't forget to apply touchevent on avtivity.
I asked this at the libgdx forums but didn't get a response so I was hoping y'all could help me out:
I have Actors that represent game pieces. What I'm trying to do is make it so the player can click-and-drag the tile to move it around the screen and rotate it multiple times before submitting the placeTile command. From what I understand of DragAndDrop it doesn't seem to be designed with my use case in mind so I figured I'd instead attach a dragListener listener to each game piece (code below). It works well for dragging, except I can't figure out how to set the 'minimum distance before drag starts' to 0... but that's not my main question (though any insights would be appreciated )
Anyway, the big problem comes in when I rotate the actor, and then try to drag it: At 30 degrees rotation, drag acts almost like normal: at 60 degrees, very small movements of the mouse send the actor moving in a tight circle very quickly. Another 30 degrees, the tile actor exits the screen in 1-2 frames, moving in a wide arc. If the actor is rotated clockwise, it's movements are clockwise; same pattern for counter-clockwise.
It looks like the translation of the actor is taking rotation into account; I guess my question is, is it possible to rotate an Actor/Group without the rotation affecting future translations? Alternatively, is there a better way to drag an Actor around the screen based on touch/mouse input? I included some code below: I imagine I'm screwing up something basic, but I can't figure out what:
// during initial stage creation
tileActor.setOrigin(tileActor.getWidth() / 2, tileActor.getHeight() / 2);
tileActor.addListener(new DragListener() {
public void dragStart(InputEvent event, float x, float y,
int pointer) {
chosenTileActor = event.getTarget();
}
public void drag(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
target.translate(x, y);
}
});
And for the listener that deals with rotation via scrolling mouse wheel:
multiplexer.addProcessor(new InputAdapter() {
#Override
public boolean scrolled(int amt) {
if (chosenTileActor == null)
return false;
else
chosenTileActor.rotate(amt * 30);
return true;
}
});
Any pointers? Am I even going the right direction by using DragListener?
Thanks for reading!
Instead of translating, just set the actor's position directly to the stage coordinates of your drag event:
tileActor.addListener(new DragListener() {
private float offsetX, offsetY;
#Override
public void dragStart(InputEvent event, float x, float y, int pointer) {
Actor target = event.getTarget();
this.offsetX = event.getStageX() - target.getX();
this.offsetY = event.getStageY() - target.getY();
}
#Override
public void drag(InputEvent event, float x, float y, int pointer) {
event.getTarget().setPosition(event.getStageX() - offsetX, event.getStageY() - offsetY);
}
});
I'm computing the offsets in dragStart so that the actor doesn't immediately jump to wherever I clicked when I started dragging (making the drags relative to my mouse). Tested this and it works with any rotation.
I'm converting a Swing/Graphics2D app with a lot of custom painting to a JavaFX2 app. Although I absolutely love the new API, I seem to have a performance problem when painting an ellipse that I want to paint below the mouse cursor wherever the mouse is moved. When I move my mouse in a steady way, not ridicously fast, I notice the ellipse is always drawn a few centimeters behind on the mouse trail, and only catches up when I stop moving the cursor. This in a scenegraph with only a handful nodes. In my Swing app I didn't have that problem.
I'm wondering if this is the correct approach for drawing a shape where the mousecursor is?
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.EllipseBuilder;
import javafx.stage.Stage;
public class TestApp extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
Pane p = new Pane();
final Ellipse ellipse = EllipseBuilder.create().radiusX(10).radiusY(10).fill(Color.RED).build();
p.getChildren().add(ellipse);
p.setOnMouseMoved(new EventHandler<MouseEvent>() {
public void handle(MouseEvent event) {
ellipse.setCenterX(event.getX());
ellipse.setCenterY(event.getY());
}
});
Scene scene = SceneBuilder.create().root(p).width(1024d).height(768d).build();
primaryStage.setScene(scene);
primaryStage.show();
}
}
Small update: I upgraded to JavaFX 2.2 and Java7u6 (on Windows 7 64bit), doesn't seem to make a difference though.
Here is some code I use to allow a Label to be dragged around in a Pane.
I don't notice any significant lag behind the mouse trail with it.
// allow the label to be dragged around.
final Delta dragDelta = new Delta();
label.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
// record a delta distance for the drag and drop operation.
dragDelta.x = label.getLayoutX() - mouseEvent.getSceneX();
dragDelta.y = label.getLayoutY() - mouseEvent.getSceneY();
label.setCursor(Cursor.MOVE);
}
});
label.setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
}
});
label.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setLayoutX(mouseEvent.getSceneX() + dragDelta.x);
label.setLayoutY(mouseEvent.getSceneY() + dragDelta.y);
}
});
label.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
}
});
. . .
// records relative x and y co-ordinates.
class Delta { double x, y; }
Here is a small complete example app using the above code.
Update
The above example, will still lag the object being dragged behind the cursor when the objects being dragged are small.
An alternate approach is to use an ImageCursor comprising of a MousePointer superimposed over the an image representation of the node being dragged, then hide and show the actual node at the start and completion of the drag. This means that the node drag rendering will not lag the cursor (as the image representation of the node is now the cursor). However this approach does have drawbacks => there are restrictions on the size and format of ImageCursors, plus you need to convert your Node to an Image to place it in an ImageCursor, for which you may need advanced Node => Image conversion operations only to available in JavaFX 2.2+.
The lag that you're describing (between your mouse and the dragged shape) is a known JavaFX bug:
https://bugs.openjdk.java.net/browse/JDK-8087922
You can work around it (on Windows, at least) by using an undocumented JVM flag:
-Djavafx.animation.fullspeed=true
This flag is normally for internal performance testing, which is why it is undocumented, but we've been using it for months and haven't had any problems with it so far.
EDIT:
There's another, similar way to workaround this bug that might be a little easier on CPU usage. Simply turn off Prism's vertical sync:
-Dprism.vsync=false
In our app, either of these workarounds solves the lag; there's no need to do both.
To me it doesn't look like a question of painting performance, but how the sequence of mouse events is generated. The events are not generated in real time, some are skipped, when the mouse moves fast. For the most applications this will be the sufficent way. The mouse pointer moves in real time without any time lag.
If you don't want this effect you will have to listen to the mouse pointer directly or find a way to get the events in higher density. I don't know how myself.
There's this "cacheHint" property, available on all Nodes and that may help ?
http://docs.oracle.com/javafx/2/api/javafx/scene/Node.html#cacheHintProperty
Under certain circumstances, such as animating nodes that are very expensive to render, it is desirable to be able to perform transformations on the node without having to regenerate the cached bitmap. An option in such cases is to perform the transforms on the cached bitmap itself.
This technique can provide a dramatic improvement to animation performance, though may also result in a reduction in visual quality. The cacheHint variable provides a hint to the system about how and when that trade-off (visual quality for animation performance) is acceptable.
If your ellipse remains the same the whole time, but is redrawn every time you move it by one pixel, this seems to be a huge slowdown.
I was having the same problem while trying to make nodes on a chart draggable. I fixed it by calling chart.setAnimated(false); In my case the lag was being caused by JavaFX applying a nice smooth animation to the changes my code was making.
here is the code to drag and drop label using mouse in javafx
#FXML
public void lblDragMousePressed(MouseEvent m)
{
System.out.println("Mouse is pressed");
prevLblCordX= (int) lblDragTest.getLayoutX();
prevLblCordY= (int) lblDragTest.getLayoutY();
prevMouseCordX= (int) m.getX();
prevMouseCordY= (int) m.getY();
}
//set this method on mouse released event for lblDrag
#FXML
public void lblDragMouseReleased(MouseEvent m)
{
System.out.println("Label Dragged");
}
// set this method on Mouse Drag event for lblDrag
#FXML
public void lblDragMouseDragged(MouseEvent m)
{
diffX= (int) (m.getX()- prevMouseCordX);
diffY= (int) (m.getY()-prevMouseCordY );
int x = (int) (diffX+lblDragTest.getLayoutX()-rootAnchorPane.getLayoutX());
int y = (int) (diffY+lblDragTest.getLayoutY()-rootAnchorPane.getLayoutY());
if (y > 0 && x > 0 && y < rootAnchorPane.getHeight() && x < rootAnchorPane.getWidth())
{
lblDragTest.setLayoutX(x);
lblDragTest.setLayoutY(y);
}
}
you can use : Node.setCache(true);
(i use it with a Pane with many childrens like a TextField)
Drag Sphere
#Override
public void initialize(URL location, ResourceBundle resources) {
super.initialize(location, resources);
labelTableName.setText("Table Categories");
final PhongMaterial blueMaterial = new PhongMaterial();
blueMaterial.setDiffuseColor(Color.BLUE);
blueMaterial.setSpecularColor(Color.LIGHTBLUE);
final Sphere sphere = new Sphere(50);
sphere.setMaterial(blueMaterial);
final Measure dragMeasure = new Measure();
final Measure position = new Measure();
sphere.setOnMousePressed(mouseEvent -> {
dragMeasure.x = mouseEvent.getSceneX() - position.x;
dragMeasure.y = mouseEvent.getSceneY() - position.y;
sphere.setCursor(Cursor.MOVE);
});
sphere.setOnMouseDragged(mouseEvent -> {
position.x = mouseEvent.getSceneX() - dragMeasure.x;
position.y = mouseEvent.getSceneY() - dragMeasure.y;
sphere.setTranslateX(position.x);
sphere.setTranslateY(position.y);
});
sphere.setOnMouseReleased(mouseEvent -> sphere.setCursor(Cursor.HAND));
sphere.setOnMouseEntered(mouseEvent -> sphere.setCursor(Cursor.HAND));
bottomHeader.getChildren().addAll( sphere);
}
class Measure {
double x, y;
public Measure() {
x = 0; y = 0;
}
}
this is modified Kotlin code based on answer from #jewelsea
var dragDelta = Delta()
var releasedDelta = Delta()
scene.setOnMousePressed {
if (releasedDelta.x > 0 && releasedDelta.y > 0) {
val offsetX = it.sceneX - releasedDelta.x
var offsetY = it.sceneY - releasedDelta.y
dragDelta.x = dragDelta.x + offsetX
dragDelta.y = dragDelta.y + offsetY
} else {
dragDelta.x = it.sceneX
dragDelta.y = it.sceneY
}
scene.cursor = Cursor.MOVE;
releasedDelta = Delta()
}
scene.setOnMouseReleased {
releasedDelta.x = it.sceneX
releasedDelta.y = it.sceneY
scene.cursor = Cursor.HAND;
}
scene.setOnMouseDragged {
scene.translateX = it.sceneX - dragDelta.x;
scene.translateY = it.sceneY - dragDelta.y;
}
scene.setOnMouseEntered {
scene.cursor = Cursor.HAND
}
I'm doing these iTunes Stanford classes, and I've been learning beginning Java. Things are going great, but they recently introduced events-and specifically MouseEvents. I've been reading the chapters in the book, and pouring through the example code, and something is just not clicking right for me...it's always that asynchronous stuff that gives me trouble :-D
Earlier, some people mentioned it was important that I mention that the "addMouseListener" is a class in the Graphics import. As far as I can tell, that just adds a blanket mouse listener to the canvas.
I'm still real new to this, so I may not be describing things as well as I should.
This is a piece of code that I have been trying to simplify in order to better understand it. Currently, it will build a red rectangle, and I can click on it and drag it along the x axis. Great!!!
import java.awt.*;
import java.awt.event.*;
import acm.graphics.*;
import acm.program.*;
/** This class displays a mouse-draggable rectangle and oval */
public class DragObject extends GraphicsProgram {
/* Build a rectangle */
public void run() {
GRect rect = new GRect(100, 100, 150, 100);
rect.setFilled(true);
rect.setColor(Color.RED);
add(rect);
addMouseListeners();
}
/** Called on mouse press to record the coordinates of the click */
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
/** Called on mouse drag to reposition the object */
public void mouseDragged(MouseEvent e) {
if((lastX) > 100){
gobj.move(e.getX() - lastX, 0);
lastX = e.getX();
lastY = e.getY();
}
}
/** Called on mouse click to move this object to the front */
public void mouseClicked(MouseEvent e) {
if (gobj != null) gobj.sendToFront();
}
/* Instance variables */
private GObject gobj; /* The object being dragged */
private double lastX; /* The last mouse X position */
private double lastY; /* The last mouse Y position */
}
If I drag the mouse off the canvas, I want the rectangle to stay within the canvas, and not move off it (the same behavior that a horizontal scroll bar would do if you moved beyond the scroll area with the mouse button still clicked). How can I do that?
I've been trying something along these lines, but it's not working right:
if ( ( lastX > (getWidth() - PADDLE_WIDTH) ) || ( lastX < PADDLE_WIDTH ) ) {
gobj.move(0, 0);
} else {
gobj.move(e.getX() - lastX, 0);
}
Your code is moving the rectangle relative to the last position of the mouse. This works fine when you are simply moving things, but for your needs when you want it to stop at the borders, you need to use absolute positioning.
// When the mouse is pressed, calculate the offset between the mouse and the rectangle
public void mousePressed(MouseEvent e) {
lastX = e.getX();
lastY = e.getY();
gobj = getElementAt(lastX, lastY);
}
public void mouseDragged(MouseEvent e) {
double newX;
// Assuming you can get the absolute X position of the object.
newX = gobj.getX() + e.getX() - lastX;
// Limit the range to fall within your canvas. Adjust for your paddle width as necessary.
newX = Math.max( 0, Math.min( newX, getWidth() ) );
// Set the new position of the paddle, assuming you can set the absolute position.
gobj.setX( newX );
lastX = e.getX();
lastY = e.getY();
}
}
This may not be quite what you want because as soon as you go off the edge, the object will stop moving, but then once you move back toward the canvas, your paddle will move immediately instead of waiting for the mouse to reach the same relative position to the paddle at which it started.
You can probably experiment to get it to do what you want.
In order to do this you will need to know the width of the Canvas object, i'm sure there will be a method that will provide this value. You can then check the current x location of the MouseEvent against the width of the canvas, and not increment the x coordinates of the shape object once you are past the width of the canvas. Depending on how much of the shape you want to remain in the canvas, you may need to take into account the width of the shape object as well.
One thing that helps me when dealing w/ animation and moving objects in a gui is drawing out a few scenarios on paper, and noting how the coordinates change.