Basically, I want to rotate an ImageView by 90 degrees when the user hits R on the keyboard.
#FXML
private Image arrow;
#FXML
private ImageView arrowDirection;
#FXML
public void arrowRotation(KeyEvent keyEvent)
{
if(keyEvent.getCode() == KeyCode.R)
arrowDirection.setRotate(90);
}
Your question is a bit unspecific, so I assumed that you want an EventHandler that is added to the pane in which the image view rests, and that you want to set it in the Controller. This is my solution:
import java.net.URL;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.Pane;
public class FXMLDocumentController implements Initializable {
#FXML
Pane pane;
#FXML
ImageView imgView;
public void arrowRotation(KeyEvent event){
if(event.getCode().equals(KeyCode.R))
imgView.setRotate(90);
}
#Override
public void initialize(URL url, ResourceBundle rb) {
Image img = new Image("https://upload.wikimedia.org/wikipedia/commons/thumb/7/7c/Javafx-layout-classes.svg/1000px-Javafx-layout-classes.svg.png");
imgView.setImage(img);
pane.sceneProperty().addListener(new ChangeListener<Scene>() {
#Override
public void changed(ObservableValue<? extends Scene> observable, Scene oldValue, Scene newValue) {
if(newValue != null){
pane.requestFocus();
}
}
});
pane.setOnKeyPressed(new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
arrowRotation(event);
}
});
}
}
Of course, this needs an FXML that holds an ImageView inside of a Pane, but that can easily be adjusted to fit your needs.
The function that performs the rotation works pretty much like you thought, but I would compare using equals whenever possible. I added a listener to the sceneProperty of the pane to let the pane request the focus once the whole thing is loaded and the scene is set. This means that pressing a key will actually trigger KeyEventHandlers that are registered on the pane. Registering the EventHandler is straight forward again. Hope that helps :)
Related
I'm beginner of javafx:
I am developing a graphical user interface using javafx and Scene builder that has a coordinate plane with x axis and y axis. It should be like this: https://postimg.cc/image/98k9mvnb3/
when someone do a mouse click anywhere on this coordinate plane it will show the coordinate point(x,y) of the pixel on the console and there will a mark(like point or some text will be written) on the same place where mouse clicked.
For implementing these things I have to use canvas & i'm able to get the coordinate point but i'm not getting how to draw the coordinate plane and how to write something on the pixel where mouse clicked.
Here is my code:
Controller Class
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
public class AxisController implements Initializable{
#FXML
private AnchorPane anchr;
#FXML
private Canvas canvas;
#Override
public void initialize(URL location, ResourceBundle resources) {
assert canvas != null : "fx:id=\"canvas\" was not injected: check your FXML file 'AxisFxml.fxml'.";
}
#FXML
private void handleMouse(MouseEvent event){
System.out.println(event.getX());
System.out.println(event.getY());
}
}
Main Class:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
public class AxisMain extends Application {
#Override
public void start(Stage primaryStage) {
try {
AnchorPane root = FXMLLoader.load(getClass().getResource("/application/AxisFxml.fxml"));
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
Have you tried something as simple as putting some code like this into your handleMouse method?
GraphicsContext gc = canvas.getGraphicsContext2D();
gc.setFill(Color.BLUE);
gc.fillRect(event.getX()-5,event.getY()-5,10,10);
This assumes of course that you have also attached this method to the canvas so that you actually get the events in the right coordinate system.
I need either a Label or a Text for my project. I need the label so that ellipsis can be used. The problem though, is when I try to use a FadeTransition, and play it, the label gets slightly darker at the start. Here is some demo code:
package com.neonorb.test;
import javafx.animation.FadeTransition;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.io.IOException;
/**
* Created by chris on 7/20/15.
*/
public class Test extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
Label label = new Label("hello");
//Text label = new Text("hello);//Using Text instead of Label does not cause the weird behavior
FadeTransition fadeTransition = new FadeTransition(Duration.seconds(3), label);
fadeTransition.setFromValue(1.0);
fadeTransition.setToValue(0.0);
fadeTransition.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
label.setOpacity(1.0);
}
});
Button button = new Button("play");
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
fadeTransition.play();
}
});
BorderPane borderPane = new BorderPane();
borderPane.setCenter(label);
borderPane.setBottom(button);
primaryStage.setScene(new Scene(borderPane));
primaryStage.show();
}
}
So I either need a fix to this problem, or a way to use ellipsis in Text. Any ideas?
Set the opacity of the label to 0.99 initially:
label.setOpacity(0.99);
Also change the code inside setOnFinished method in the same way. Then, set the starting value of the fade transition to 0.99:
fadeTransition.setFromValue(0.99);
I know this is not the solution you are looking for, but this solution prevents the label from abruptly getting darker at the start. That is because the label actually starts with that darker status.
I have a TextArea that doesn't scroll down when I add text in it. I thought using this answer, but my TextArea is connected to a StringProperty like this :
consoleTextArea.textProperty().bind(textRecu);
So the answer doesn't work for me, is there another way to make my TextArea scroll down every time I update it by the binding?
Here is fast demo of what i meant in comment about adding listener to the textRecu. Yep consoleTextArea.textProperty() can't be changed because of a binding. But textRecu has no binding => can be changed and we can add listener to it.
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
private StringProperty textRecu = new SimpleStringProperty();
private TextArea consoleTextArea = new TextArea();
#Override
public void start(Stage primaryStage) throws Exception{
VBox root = new VBox();
Button button = new Button("Add some text");
button.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
//here you change textRecu and not consoleTextArea.textProperty()
textRecu.setValue(textRecu.getValue() +"New Line\n");
}
});
root.getChildren().addAll(consoleTextArea, button);
consoleTextArea.textProperty().bind(textRecu);
//here you also add listener to the textRecu
textRecu.addListener(new ChangeListener<Object>() {
#Override
public void changed(ObservableValue<?> observable, Object oldValue,
Object newValue) {
// from stackoverflow.com/a/30264399/1032167
// for some reason setScrollTop will not scroll properly
//consoleTextArea.setScrollTop(Double.MAX_VALUE);
consoleTextArea.selectPositionCaret(consoleTextArea.getLength());
consoleTextArea.deselect();
}
});
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I created a little View and put them into a stage. I set the stage APPLICATION_MODAL:
stage.initModality(Modality.APPLICATION_MODAL);
If I click on the calling Window a blocking sound appears. So this is the right action.
But is there any posibility to replace this blocking sound, with an own function? I want to close the new stage if I click on the calling window, but I really tried all what comes to my mind, even an Eventfilter for Event.ANY doesn't work:
stage.addEventFilter(Event.ANY, new EventHandler<Event>() {
#Override
public void handle(Event event) {
System.out.println("Event catched: "+event);
}
});
Is there any way to handle the click on the calling window?
When you set something to APPLICATION_MODAL, you prevent any of the events from being dispatched to ANY window. This means that your event listener is of no use. Here is what the docs say:
APPLICATION_MODAL
Defines a modal window that blocks events from being
delivered to any other application window.
My suggestion is that you disable all your components when your custom view is visible and when a click occurs on the parent window while your view is visible, close your view. This will solve the problem of explicitly setting the modality (Anyways your events are not being passed).
SSCCE:
package stack;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFieldBuilder;
import javafx.scene.effect.Reflection;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextBuilder;
import javafx.stage.Modality;
import javafx.stage.Popup;
import javafx.stage.Stage;
import javafx.stage.StageBuilder;
public class DismissPopup extends Application {
Text hello;
Scene primaryScene;
TextField f;
Stage extraStage;
Scene extraScene;
#Override
public void start(Stage primaryStage) throws Exception {
primaryScene = SceneBuilder
.create()
.width(300)
.height(300)
.root(new StackPane())
.fill(Color.BLACK)
.build();
hello = TextBuilder
.create()
.text("Hello")
.effect(new Reflection())
.build();
f = TextFieldBuilder
.create()
.promptText("Enter Some Text")
.build();
extraScene = SceneBuilder
.create()
.width(300)
.height(300)
.root(new StackPane())
.fill(Color.WHEAT)
.build();
StackPane p = (StackPane) extraScene.getRoot();
p.getChildren().addAll(hello);
p = (StackPane) primaryScene.getRoot();
p.getChildren().addAll(f);
extraStage = StageBuilder
.create()
.scene(extraScene)
.build();
extraStage.sizeToScene();
primaryScene.setOnMouseClicked(new EventHandler<MouseEvent>(){
#Override
public void handle(MouseEvent me) {
if(f.disabledProperty().get() == false){
f.setDisable(true);
f.setText("The TextField is disabled");
extraStage.show();
}else{
f.setText("The TextField is enabled");
f.setDisable(false);
extraStage.close();
}
}
});
primaryStage.setScene(primaryScene);
primaryStage.sizeToScene();
primaryStage.show();
}
public static void main(String[] args) {
Application.launch("stack.DismissPopup");
}
}
Output:
Consider this:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.SceneBuilder;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonBuilder;
import javafx.scene.control.Label;
import javafx.scene.control.LabelBuilder;
import javafx.scene.layout.FlowPaneBuilder;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageBuilder;
public class DualMonitorProblem extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) throws Exception {
Label label = LabelBuilder.create().text("I seem to be lost").build();
Scene dlgScene = SceneBuilder.create().root(label).build();
final Stage dlgStage = StageBuilder.create().scene(dlgScene).resizable(false).build();
dlgStage.initModality(Modality.APPLICATION_MODAL);
dlgStage.initOwner(stage);
Button btn = ButtonBuilder.create().text("put me on secondary monitor before clicking me").build();
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
dlgStage.showAndWait();
}
});
Button btn2 = ButtonBuilder.create().text("me too").build();
btn2.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
dlgStage.setX(stage.getX() + stage.getWidth() / 2);
dlgStage.setY(stage.getY() + stage.getHeight() / 2);
dlgStage.showAndWait();
}
});
Button btn3 = ButtonBuilder.create().text("me too").build();
btn3.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
dlgStage.centerOnScreen();
dlgStage.showAndWait();
}
});
Pane p = FlowPaneBuilder.create().children(btn, btn2, btn3).build();
Scene scene = SceneBuilder.create().root(p).width(800).height(600).build();
stage.setScene(scene);
stage.show();
}
}
Drag the stage to a secondary monitor and then click one of the buttons. When you do nothing extra the modal dialog will open on the primary monitor.
What is the easiest way to have the modal dialog open in the center of the parent stage? Stage.centerOnScreen() doesn't same to take into account a dual monitor setup. Should I consider this a bug and file it? Or is there another way to achieve this?
There a static method
static ObservableList<Screen> getScreens() The observable list of currently available Screens.
in Screen class. And
static Screen getPrimary() The primary Screen.
use them to determine, which screen is not a Primary.
You can use it to determine, which screens you have. And use Window. setX and setY methods - to position a window - just a simple math.
Also, I checked in JFX jira, that there are no features about adding any method method centering with a screen as parameter. So, you can file an RFE, to add method centerOnScreen(Screen)...