I'm looking for some feedback/insight into the use of Javas ability to clone events.
/*create an eventhandler for the mouse click when on the imagetoview
* This event handler is bound to the mouse_clicked event
*/
imagetoview.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
//define a public override to set the functionality of the new
//mouse click event handler
#Override public void handle(MouseEvent e) {
Object clone = e.clone();
}
});
Why would I use this ability if i can access all of the event data from the original?
Related
Seen this somewhere in StackOverflow. Just want to know how it works...
public void mouseClicked(MouseEvent e){
int x = e.getX();
int y = e.getY();
}
x and y are coordinates and can be shown to screen using JLabel, but the method name is mouseClicked. How does java know the mouse has been clicked?
(Hope this makes sense)...
The method mouseClicked is likely from java.awt.event.MouseListener interface (https://docs.oracle.com/javase/7/docs/api/java/awt/event/MouseListener.html)
A listener is a type of callback that follows the observer pattern, something happens, you get notified.
You can attach listener to items that support it. For example:
MouseListener listener = new MouseListener() { /* see example code below */ };
JLabel label = new JLabel("This is a clickable lable");
label.addMouseListener(listener);
See the following answer to get more info and reference to reading articles.
https://stackoverflow.com/a/17415300/132121
#transformer here is an empty implementation of the MouseListener you would create in Java code.
MouseListener listener = new MouseListener() {
#Override
public void mouseClicked(MouseEvent e) {
// This is the method where you would get your callback
// whenever somebody clicked on the view that has this listener
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
};
This is an event handler. In order for it to work, it has to be "attached" to something in the front end (most likely a button, but it could be another UI element too).
Exactly how this works depends on which UI framework is being used, but since this is Java I assume it's most likely AWT. You can find more details in tutorials, e.g. here.
Incidentally, how significant the name is depends on which UI framework this is from. In Android, WPF, and ASP.NET, for example, the name of event handlers could theoretically be anything, it's mostly just a matter of convention (not actual requirement) what you call it. (Obviously, you have to be consistent with the name, though). As pointed out in the comments, though, in AWT this name is actually likely significant due to the class that contains it implementing an interface.
Making progress with my board game, so on a mouse click, some tasks will be performed like playing some animations, receiving new panes for the board (I asked about this my last question, works great now), making changes to player data etc.
So, here is my EventHandler
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
}
});
Here grid is a GridPane object, which contains other panes for each cell of the board, panes for animation etc.
One mouse event would take 2-3 seconds to be handled. While this is going on, I saw that another mouse click would also start being handled parallel with the one already ongoing.
What I want is that another mouse event should not be processed until the first one has been completed. It would be better if, any clicks if received would be discarded.
I tried to use threading:
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
Thread t = new Thread(new Runnable() {
public void run() {
//completing the necessary tasks here
}
});
t.start();
t.join();
}
});
But GUI changes would not occur if I use threading (don't know why, I tested it in many different ways, always failed to make GUI changes), so this would not work I guess.
So what could I do to not take in any mouse clicks while one mouse event is being handled? I think this question doesn't require more code, but if necessary, I would edit and add some code.
EDIT:
class Animations {
public Pane getAnimationPane() {
//returns Pane which would be used for transition
}
public void playAnimation() {
//called only when the timeline transition is to be played
}
}
So I made an array of the Animations class because I need a lot of such panes and need to keep a track of each one of them.
When I need to play the animation, I call the playAnimation() method.
Now this playAnimation() method is like an animation-inception.
After an animation on this pane is completed, changes are made to the board according to the player's progress, and if required... this playAnimation() method would call the playAnimation() method of several other Animation objects.
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
someAnimationObject.playAnimation();
}
});
This could go as deep as 10-20 (more, if the grid size is large) other Animation objects being used for their playAnimation().
EDIT 2:
The playAnimation() of Animations could call playAnimation() on 0-8 other Animations objects. And this could keep on going.
EDIT 3:
I finally sovled my problem, this is how I did it.
I added a SimpleBooleanProperty isAnimating to my Animations class (instance variable, i.e. non-static), set it to false by default.
When the timeline animation begins playing, I set it to true, and when the timeline animation is completed, I set it to false.
In the main class (say Game), I added a BooleanBinding anyAnimating.
I binded anyAnimating to isAnimating of each Animations object using the array I said I create.
anyAnimating = Bindings.or(anyAnimating, AnimationsObj[i][j].isAnimatingProperty());
And finally I used the value of anyAnimating as a flag in the MouseEvent EventHandler as Brad suggested.
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
if(!anyAnimating.get()){
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
}
}
});
I'm not really sure on the details here because there aren't a lot of details in your question about how the animations are managed, but this should give you the general idea.
Add a flag to your Animations class indicating if animations are in progress:
class Animations {
private ReadOnlyBooleanWrapper animating = new ReadOnlyBooleanWrapper() ;
public ReadOnlyBooleanProperty animatingProperty() {
return animating.getReadOnlyProperty();
}
public boolean isAnimating() {
return animatingProperty().get();
}
public Pane getAnimationPane() {
//returns Pane which would be used for transition
}
public void playAnimation() {
//called only when the timeline transition is to be played
Animation animation = ... ;
animation.statusProperty().addListener((obs, oldStatus, newStatus) ->
animating.set(newStatus == Animation.Status.RUNNING));
animation.play(); // etc
}
}
Now you can do
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
if (! someAnimationObject.isAnimating()) {
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
someAnimationObject.playAnimation();
}
}
});
or perhaps
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
grid.setDisable(true);
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
someAnimationObject.animatingProperty().addListener((obs, wasAnimating, isNowAnimating) -> {
if (! isNowAnimating) {
grid.setDisable(false);
}
});
someAnimationObject.playAnimation();
}
});
Another approach would be to let playAnimation() accept a callback to execute when the animation is complete (or when all the animations are complete, if you are executing multiple animations).
This would look something like:
class Animations {
public Pane getAnimationPane(Runnable finishedCallback) {
//returns Pane which would be used for transition
}
public void playAnimation() {
//called only when the timeline transition is to be played
Animation animation = ... ;
animation.statusProperty().addListener((obs, oldStatus, newStatus) -> {
if (newStatus == Animation.Status.STOPPED) {
finishedCallback.run();
}
});
animation.play(); // etc
}
}
Then your event handling code could do:
// declared at instance level:
private boolean animating = false ;
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
if (! animating) {
animating = true ;
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
someAnimationObject.playAnimation( () -> {animating = false ;});
}
}
});
or
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
grid.setDisable(true);
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
someAnimationObject.playAnimation( () -> {grid.setDisable(false);});
}
});
I think this could be handled a couple of different ways. One way that comes to mind is to use a simple semaphore. To do this, you would simply declare a boolean field at the top of your class called something like isUpdating.
private boolean isUpdating = false;
When you enter your mouse click, the first thing the method would do is check to see if isUpdating is false. If so, the next thing would be to set isUpdating to true, do the work, and then set it to false again.
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
if(!isUpdating){
isUpdating = true;
//make changes to player data
//receive new panes for the board
//make some gui changes
//play some animations
isUpdating = false;
}
}
});
Regarding your comment on why the thread isn't updating your GUI, this is because your the changes in one thread while your UI repainting is handled in another thread. If your anonymous thread/mouselistener is in the same class as the UI that is being updated, you can create a refresh method and call it at the end of you worker thread.
UI Class:
private void refresh(){
this.revalidate();
this.repaint();
}
Worker Thread:
grid.setOnMouseReleased(new EventHandler<MouseEvent> () {
public void handle(MouseEvent me) {
Thread t = new Thread(new Runnable() {
public void run() {
//completing the necessary tasks here
refresh();
}
});
t.start();
t.join();
}
});
If your UI is in different class, you could create an external action listener and register your UI components to it.
public interface GridListener {
public void gridUpdated();
}
Then add your listener to an object that would need access:
public class MyUIClass() {
private List<GridListener> listeners;
}
public MyUIClass(){
listeners = new ArrayList<GridListener>();
}
public void addListener(GridListener listener) {
listeners.add(listener);
}
This may be more code then you're looking to write to remedy your current problem, these are just some suggestions I thought of while reading your question.
Scenario. I have a graph on which i can perform panning using right click. This works perfectly. Then I added menus on right click perfectly.
Problem. Now right click menus are shown even on mouse release after drag operation completions.
Is there a way to differentiate mouse release and mouse release after mouse drag in Java Swing or JavaFX?
Mouse events are generated independently of one another.
I assume you panning code works with a combination of mousePressed/mouseMoved.
So you need to add some logic to indicate that you are in "panning mode". So if you have a mousePressed followed by a mouseMoved you set a Boolean variable to indicate "panning" mode.
Then in the mouseReleased code you need to check the variable. If "panning mode" then set "panning mode" off and return. Otherwise you are in "popup mode" so you can display the popup.
Since event.isDragDetect() is always true, so I was not able to differentiate events. I created a java class to store a boolean value. This was needed to modify final object state in inner class and was not possible using Boolean wrapper class. Later I am modifying final objects states based on mouse clicked and mouse dragged. I am also checking if mouse release is after drag or without drag as below:-
private void addRightClickMenu() {
final SubMarineBooleanUtilityClass showMenu = new SubMarineBooleanUtilityClass(true);
final MenuItem graphButton1 = new MenuItem("Save Graph as..");
graphButton1.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
saveGraphAs();
}
});
final MenuItem graphButton2 = new MenuItem("Reset Graph");
graphButton2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
resetGraph(controlGraph1.getLineChart(), controlGraph2.getLineChart());
}
});
final ContextMenu menu = new ContextMenu(graphButton1, graphButton2);
//Mouse Drag operation cycle=Mouse click+Mouse dragged+Mouse release
getAnchorPaneGraphView().setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(true);
}
}
});
getAnchorPaneGraphView().setOnMouseReleased(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton()) && showMenu.isValueBoolean()) {
menu.show(getAnchorPaneGraphView(), event.getScreenX(), event.getScreenY());
}
}
});
getAnchorPaneGraphView().setOnMouseDragged(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (MouseButton.SECONDARY.equals(event.getButton())) {
showMenu.setValueBoolean(false);
}
}
});
}
public class SubMarineBooleanUtilityClass {
boolean valueBoolean=false;
/**
* #return boolean
*/
public boolean isValueBoolean() {
return valueBoolean;
}
/**
* Constructor passing a boolean values
* #param value
*/
public SubMarineBooleanUtilityClass(boolean value){
this.valueBoolean=value;
}
/**set boolean value
* #param value
*/
public void setValueBoolean(boolean valueBoolean) {
this.valueBoolean = valueBoolean;
}
}
I have many ItemWidget which extends a Composite. If a click event is received on one of the items a change event should be fired and other item widgets should receive this event.
It tried the following:
public class ItemWidget extends Composite implements HasChangeHandlers {
FocusPanel focusPanel = new FocusPanel();
public ItemWidget() {
Label label = new Label("click me");
focusPanel.add(label);
initWidget(focusPanel);
focusPanel.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// inform other items
fireChange();
}
});
addChangeHandler(new ChangeHandler() {
#Override
public void onChange(ChangeEvent event) {
GWT.log("ChangeEvent received");
}
});
}
private void fireChange() {
GWT.log("fire event");
NativeEvent nativeEvent = Document.get().createChangeEvent();
ChangeEvent.fireNativeEvent(nativeEvent, this);
}
#Override
public HandlerRegistration addChangeHandler(ChangeHandler handler) {
return addDomHandler(handler, ChangeEvent.getType());
}
}
Using the above code only the item which is clicked receives the ChangeEvent.
How can I receive the ChangeEvent on all the other item widgets too?
Typically, when an event fires, a presenter/Activity makes the necessary changes to the other widgets.
If you want multiple copies of you widget to listen to the same event, you may be better off using the EventBus, especially if you use this pattern more than once:
How to use the GWT EventBus
Then each of your ItemWidget can fire a custom event that all copies of this widget listen to.
By looking at the API quickly I think your problem comes from your implementation of
public void fireEvent(GwtEvent<?> event)
You did not override it in your composite view. You did not create any mechanism to send the event to the other widgets.
Have a deeper look at the method fireNativeEvent
http://www.gwtproject.org/javadoc/latest/com/google/gwt/event/dom/client/DomEvent.html
When you use ChangeEvent.fireNativeEvent the second param hasHandlers should be a kind of event bus.
To do what you want to do I think you need all the items to be on this eventBus.
Suppose I want to have my program to react same way, say, navigate to next record, in response to different events, including pressing a key, clicking GUI button, selecting menu item and so on.
This was done with "actions" in Swing.
Can I materialize this concept in some program object in JavaFX?
Or I should make a porridge of interacting objects?
Action is still there in JavaFX. Example belows how to create an action, bind it to a keyboard shortcut and share between two different elements.
Button go = new Button("Go");
EventHandler<ActionEvent> goAction = new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
browser.load(location.getText(), new Runnable() {
#Override
public void run() {
System.out.println("---------------");
System.out.println(browser.getHTML());
}
});
}
};
...
MenuItem menuItem = new MenuItem("Go!");
menuItem.setAccelerator(new KeyCodeCombination(KeyCode.G, KeyCombination.CONTROL_DOWN));
go.setOnAction(goAction);
menuItem.setOnAction(goAction);
JavaFX provides many events. You also do this with setOn() method:
button.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent t) {
// code here
}
});