JavaFx: Access an object from Event handler - java

I want to make a program in JavaFX that contains a button which, when clicked, a circle gets created and added to an ArrayList of shapes. The following is my code:
createCircleBtn.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
Circle circle1 = new Circle();
shapes.add(circle1);
circle1.setCenterX(event.getX());
circle1.setCenterY(event.getY());
circle1.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
// doesn't work because "circle1" must be declared final (constant)
circle1.setCenterX(event.getX());
// "this" doesn't refer to "circle1"
this.setCenterY(event.getY());
}
});
mainPane.getChildren().add(circle1);
}
});
My question is - How can I access "circle1" from inside handle method?
In JavaScript we use e.currentTarget.
I'm unable to declare "circle1" final because I will need to change it afterwards.

Nowhere in the code you show do you reassign circle1, so you can just declare it as final:
createCircleBtn.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
final Circle circle1 = new Circle();
shapes.add(circle1);
circle1.setCenterX(event.getX());
circle1.setCenterY(event.getY());
circle1.setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
circle1.setCenterX(event.getX());
}
});
mainPane.getChildren().add(circle1);
}
});
Note that in Java 8 your code would compile just as you have it, because circle1 is effectively final (meaning that it is only assigned once and is never reassigned).

Use event.getSource(), by this can write a common event handler for multiple cicrles without worrying which exact one was clicked:
EventHandler<MouseEvent> handler = new EventHandler<>()
{
#Override
public void handle( MouseEvent event )
{
if (event.getSource() instanceof Circle) { // to be on safe side, you may
// remove this if-statement
// if you are sure
Circle c = (Circle) event.getSource();
c.setCenterX( event.getX() );
}
}
};
// use it on multiple circles
circle1.setOnMouseDragged(handler);
circle2.setOnMouseDragged(handler);
...
circleN.setOnMouseDragged(handler);

Related

I can't access an instance variable in an if statement in Java

I am trying to simplify the Javax swing graphics classes in order to make it easier for other people to get into Java graphics, but I am facing a problem with testing it.
Keep in mind, that I am writing the main method as a user of the code and not the developer. I need answers that will change the code of the class methods and not the main method.
What my main method code is supposed to do is print 'hovering' when the user hovers over the button. However, when I add a SOP statement before the if statement, it works...
The method for the mouse hovering is in the Button class.
Here is my main method code -
public static void main(String[] args) {
GraphWin win = new GraphWin(1000, 1000, "Graphics Window - Test");
win.show();
Button button = new Button(new Point(380, 300), new Point(620, 400));
button.draw(win);
enter code herewhile(true) {
//System.out.println(button.hovering);
if(button.hovering) {
System.out.println("hovering");
}
}
}
And here is my code for the Button class -
public class Button implements MouseListener{
public JButton button;
public boolean clicked = false, hovering = false, pressed = false;
public Button(Point p, Point p2) { //This is the default constructor of the button with only 2 points specified
this.button = new JButton();
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text) { //This constructor requires text to be displayed`enter code here`
this.button = new JButton(text);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(String icon, Point p, Point p2) { //This constructor sets an Icon for the button
this.button = new JButton();
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public Button(Point p, Point p2, String text, String icon) { //Here, both the text and Icon is specified
this.button = new JButton(text);
this.setIcon(icon);
this.setBounds(p, p2);
this.button.addMouseListener(this);
this.setBorderVisible(false);}
public void draw(GraphWin win) {
win.window.add(this.button);}
public void setBounds(Point p, Point p2) {
this.button.setBounds(p.x, p.y, p2.x - p.x, p2.y - p.y);
}
public void setEnabled(boolean enable) {
this.button.setEnabled(enable);}
public void disable() {
this.button.setEnabled(false);}
public void enable() {
this.button.setEnabled(true);
}
public void setColor(Color color) {
this.button.setBackground(color);}
public void setColor(String color) {
this.button.setBackground(Color.decode(color));}
public void setText(String text) {
this.button.setText(text);}
public void setIcon(String icon) {
File imageCheck = new File(icon);
if(!imageCheck.exists())
System.out.println("Image file not found!");
else
this.button.setIcon(new ImageIcon(icon));
}
public void resizeIcon(String icon, int width, int height) {
Image img = new ImageIcon(icon).getImage();
img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
this.button.setIcon(new ImageIcon(img));
}
public void setCustomMargins(int top, int bottom, int left, int right) {
this.button.setMargin(new Insets(top, left, bottom, right));}
public void setMargins(int m) {
this.button.setMargin(new Insets(m, m, m, m));}
public void setLabel(String label) {
this.button.setToolTipText(label);
}
public void setBorderVisible(boolean border) {
this.button.setBorderPainted(border);}
public void setOpaque(boolean opaque) {
this.button.setContentAreaFilled(opaque);}
#Override
public void mouseEntered(MouseEvent arg0) {
this.hovering = true;
System.out.println(1);
}
#Override
public void mouseExited(MouseEvent arg0) {
this.hovering = false;
}
#Override
public void mousePressed(MouseEvent arg0) {
this.pressed = true;
}
#Override
public void mouseReleased(MouseEvent arg0) {
this.pressed = false;
}
#Override
public void mouseClicked(MouseEvent e) {
this.clicked = true;
System.out.println(1);
}
}
This sort of thing is usually to do with threading.
Events in Swing are dispatched on the AWT Event Dispatch Thread (EDT). In order to be thread-safe, practically everything dealing with Swing/AWT should be done on the EDT.
In your case, there is no kind of locking between the variable being set and read. Adding a println causes a pause (with all sorts of memory barriers or whatnot) that happens to allow the program to run in the desired sequence.
You've probably seen main methods written to pass execution straight over to the AWT.
class MyGUI {
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(MyGUI::go);
}
private static void go() {
...
It might be better to supply the main class yourself, implemented such that it takes the application class as an argument and passes execution on once everything is setup. Whilst traditionally command lines use a main static method/function, everywhere else subtypes: Applets, Servlets, etc.
best approach would be to use a isHovering() method but educated guess on the behavior inside a while(true) with or without a Sysout might be related to a compiler optimisation. Might be fixed by putting the hovering variable as transient

How can I write my code more efficiently for use with different types of JavaFX GUI components?

I am making a basic JavaFX GUI, where I would like a label to change when I am hovering over different types of GUI components.
For example below, I want my label to change text from 'Bored' to 'Hovered!' when I hover over either a ComboBox, TextField, Button, Circle etc
Instead of repeating the same code over again, I am guessing I could create a method with the object as the input argument, but I can't seem to quite get it right.
Essentially, is there an elegant way I can adapt this code so that it can be reusable for all types of different JavaFX GUI components?
The following code currently does exactly what I describe, but is something that I would like to be more efficient.
Thanks.
comboBox.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
}
});
comboBox.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.MOVE);
label.setText("Bored");
}
});
textField.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
}
});
textField.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.MOVE);
label.setText("Bored");
}
});
button.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
}
});
button.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.MOVE);
label.setText("Bored");
}
});
circle.setOnMouseEntered(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
}
});
circle.setOnMouseExited(new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.MOVE);
label.setText("Bored");
}
});
You can easily use a helper method to register all those listeners. Furthermore you can reuse the listeners, since they contain the same code for all of the nodes:
private static void registerListeners(final Label label, Node... nodes) {
final EventHandler<MouseEvent> enteredHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
}
};
final EventHandler<MouseEvent> exitedHandler = new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent mouseEvent) {
label.setCursor(Cursor.MOVE);
label.setText("Bored");
}
};
// add listeners to all nodes
for (Node n : nodes) {
n.setOnMouseEntered(enteredHandler);
n.setOnMouseExited(exitedHandler);
}
}
registerListeners(label, comboBox, textField, button);
Note that you cannot handle the MOUSE_ENTERED and the MOUSE_EXITED event on a parent node, since the event is not passed through the hierarchy. The only way to make this work for arbitrary children would be to handle the MOUSE_MOVED event on the parent. In this case you cannot be sure the intersected node is one of the nodes you want to allow. It could be a child. Therefore you need to iterate through the hierarchy yourself:
commonAncestor.setOnMouseEntered(evt -> {
Node n = evt.getPickResult().getIntersectedNode();
while (n != commonAncestor) {
if (checkNode(n)) {
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
return;
}
n = n.getParent();
}
label.setCursor(Cursor.MOVE);
label.setText("Bored");
});
I don't really know if this was the best answer. But you can try this:
#Override
public void initialize(URL location, ResourceBundle resources) {
combobox.setOnMouseEntered(this);
combobox.setOnMouseExited(this);
textfield.setOnMouseEntered(this);
textfield.setOnMouseExited(this);
button.setOnMouseEntered(this);
button.setOnMouseExited(this);
}
#Override
public void handle(MouseEvent event) {
switch (event.getEventType().getName()){
case "MOUSE_ENTERED":
label.setCursor(Cursor.HAND);
label.setText("Hovered!");
break;
case "MOUSE_EXITED":
label.setCursor(Cursor.MOVE);
label.setText("Bored");
break;
}
}
Your controller must implement EventHandler

In JavaFx, how to get parameters from timeline?

I create a code using timeline to implement a crawl effect.
Now I would like to use KeyEvent to add pause and reverse function. The pause function can be accomplished using timeline.pause(); but for reverse function, I need to get two parameters. My codes for the timeline:
Timeline tl = new Timeline();
tl.setCycleCount(Animation.INDEFINITE);
KeyFrame moveText = new KeyFrame(Duration.seconds(.0400),
new EventHandler<ActionEvent>() {
public void handle(ActionEvent event) {
double a,b;
a=0.9;
b = 0.5*(1-0.99)*a;
shiftAndScale(group, b, upperStep, 0.99, 0.99);
}
});
tl.getKeyFrames().add(moveText);
tl.play();
Then I add the KeyEvent to pause and reverse:
scene.onKeyPressedProperty().set(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent ke) {
if (ke.getCode() == KeyCode.DOWN) {
tl.pause();
}
if (ke.getCode() == KeyCode.LEFT) {
// reverse function, but requires the current a and b;
}
}
});
Therefore, when I pause the timeline, how to let the program return the variables a and b?
Make double a and b a field or set it into another variable accessible to KeyEvent

Adding mouse event for secondary mouse button Javafx

so i have this anchorpane where i wish to add a mouse listner for the Secondary mouse key ive tried the following but i keep getting an error anyone know what the problem is?
mainDisplayPanel.addEventHandler(MouseButton.SECONDARY, new EventHandler<MouseButton>() {
#Override
public void handle(MouseButton event) {
System.out.Println("Works");
}
});
for the record i have also tried this:
mainDisplayPanel.addEventHandler(MouseButton.SECONDARY, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
System.out.println("WOrks");
}
});
Stack trace:
Bound mismatch: The generic method addEventHandler(EventType,
EventHandler) of type Node is not applicable for the
arguments (MouseButton, new EventHandler(){}). The
inferred type MouseButton&Event is not a valid substitute for the
bounded parameter
And the other:
Bound mismatch: The type MouseButton is not a valid substitute for the bounded parameter of the type EventHandler
There is no EventType based on MouseButton.SECONDARY. You need to check the MouseEvent itself:
mainDisplayPanel.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.getButton() == MouseButton.SECONDARY) {
System.out.println("Works");
}
}
});

How to set border for events in GWT?

I've one Label in my custom FlowPanel which implements HasDoubleClickHandlers.
final Label label = new Label("Click here to write");
label.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
clicked();
}
});
final CustomFlowPanel customFlowPanel=new CustomFlowPanel();
customFlowPanel.addDoubleClickHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
if (event.getSource() instanceof FlowPanel) {
doubleClicked();
}
}
});
custoFlowPanel.add(label);
The problem is when i double click to the label doubleClicked() should not execute.
How to prevent executing doubleClicked() when label is double clicked?
Thanks in advance!!!
You could just check the DoubleClickEvent if the label was clicked and if not you call doubleClicked().
customFlowPanel.addDoubleClickHandler(new DoubleClickHandler() {
#Override
public void onDoubleClick(DoubleClickEvent event) {
Element clicked = event.getNativeEvent();
if (!clicked.Equals(label.getElement())
{
doubleClicked();
}
}
});
I haven't tried it yet, but try adding a double click handler on the label and use Event.stopPropagation() on it. This prevents the event from being propagated to the parent.

Categories