I have a program in which it displays 3 random hieroglyphic images on the screen. I have added a "Refresh" button in order to refresh the hieroglyphic images. When I click the button, the images refresh and randomize properly. After the button has been clicked for the first time however, it disappears. I am almost certain it has something to do with my pane.getChildren().clear(); line, but I cant seem to figure it out. Any tips or advice?
If I have posted incorrectly or without using proper guidelines, I apologize. This is one of my first posts.
Here is my code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.image.ImageView;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
public class Lab6a extends Application {
#Override
public void start(Stage myStage) {
//Create an HBox layout.
HBox hBox1 = new HBox();
//Set alignment.
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
//Create a Refresh button.
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
hBox1.getChildren().add(refresh);
//Set the title for the second window.
myStage.setTitle("Random Hieroglyphics with Refresh");
//Create a scene for the window.
Scene myScene = new Scene(hBox1, 400, 400);
//Place the scene in the second window.
myStage.setScene(myScene);
//Show the stage.
myStage.show();
}
public void getRandomHieroglyphic(HBox pane) {
pane.getChildren().clear();
//Create random generators to get a random image
int randomInt1 = (int) (Math.random() * 9) + 1;
int randomInt2 = (int) (Math.random() * 9) + 1;
int randomInt3 = (int) (Math.random() * 9) + 1;
//Create paths for the images to be called
String path1 = "Image/Hieroglyphics/h" + randomInt1 + ".png";
String path2 = "Image/Hieroglyphics/h" + randomInt2 + ".png";
String path3 = "Image/Hieroglyphics/h" + randomInt3 + ".png";
//Add the images into the pane
pane.getChildren().add(new ImageView (path1));
pane.getChildren().add(new ImageView (path2));
pane.getChildren().add(new ImageView (path3));
}
public static void main(String[] args) {
launch(args);
}
}
clear() removes all children from the HBox including the Button.
You've got 3 ImageViews and want to keep the number of ImageViews constant. This means you should not replace them, but replace the images they contain instead. Furthermore you should avoid reloading the images and load all 9 images at the start:
public class Lab6a extends Application {
private Image[] images;
private final Random random = new Random();
#Override
public void start(Stage myStage) {
// load all hieroglyphs
images = new Image[9];
for (int i = 0; i < images.length; i++) {
images[i] = new Image("Image/Hieroglyphics/h" + (i+1) + ".png");
}
// store all imageviews in array
final ImageView[] imageViews = Stream.generate(ImageView::new).limit(3).toArray(ImageView[]::new);
// set initial images
getRandomHieroglyphic(imageViews);
...
hBox1.getChildren().add(refresh);
for (ImageView iv : imageViews) {
hBox1.getChildren().add(iv);
}
...
refresh.setOnAction(e -> getRandomHieroglyphic(imageViews));
...
}
public void getRandomHieroglyphic(ImageView[] imageViews) {
for (ImageView iv : imageViews) {
iv.setImage(images[random.nextInt(images.length)]);
}
}
You're right. You Hbox has 4 cildren initially, 3 images and the button. you should add the button too in the getRandomHieroglyphic() method.
But the better way was separate the button from the images. Best is this done by Adding a BorderPane to the Scene, and put the HBox with the images only in the center and the button in the bottom area.
#Override
public void start(Stage myStage) {
HBox hBox1 = new HBox();
hBox1.setAlignment(Pos.CENTER);
getRandomHieroglyphic(hBox1);
Button refresh = new Button("Refresh");
refresh.setOnAction(e -> getRandomHieroglyphic(hBox1));
BorderPane borderPane = new BorderPane();
borderPane.getBottom().add(refresh);
myStage.setTitle("Random Hieroglyphics with Refresh");
Scene myScene = new Scene(borderPane, 400, 400);
Related
I am tryng to develop a naviagtion map system using ArcGIS Runtime for Java, and FXML files for the view part. I am currently facing zoomButtons disabling: in some zoomlevel examples, the zoomIn button should be disabled at zoomlevel = 18 and zoomout should be disabled at zoomlevel = 0. Now I am stuck while trying to disable those buttons at several zoom levels. Can anyone help me to solve this problem? You can find the attached code below.
I have already developped the zoomIn and zoomOut methods and they are working properly.
//ZoomIn Function is created
public void zoomInFunction() {
Viewpoint current = mapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
Viewpoint zoomedIn = new Viewpoint((Point) current.getTargetGeometry(), current.getTargetScale() / 2.0);
mapView.setViewpointAsync(zoomedIn);
}
//ZoomOut Function is created
public void zoomOutFunction() {
Viewpoint current = mapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
Viewpoint zoomedOut = new Viewpoint((Point) current.getTargetGeometry(), current.getTargetScale() * 2.0);
mapView.setViewpointAsync(zoomedOut);
}
// Create action event for ZoomIn Function
public void zoomInAction(ActionEvent event) {
map.zoomInFunction();
}
// Create action event for ZoomOut Function
public void zoomOutAction(ActionEvent event) {
map.zoomOutFunction();
}
Define a property (zoomLevel) and bind the disableProperty of the buttons to the zoomLevel property when it goes above or below certain maximum and minimum values.
Zoom between min and max
Zoomed out
Zoomed in
Implementation is pretty straightforward. A sample app is provided below to demonstrate the concepts involved.
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
public class BoundedZoom extends Application {
private static final int MIN_ZOOM_LEVEL = 1;
private static final int MAX_ZOOM_LEVEL = 5;
private static final int DEFAULT_ZOOM_LEVEL = 3;
private IntegerProperty zoomLevel = new SimpleIntegerProperty(DEFAULT_ZOOM_LEVEL);
public int getZoomLevel() {
return zoomLevel.get();
}
public IntegerProperty zoomLevelProperty() {
return zoomLevel;
}
public void setZoomLevel(int zoomLevel) {
this.zoomLevel.set(zoomLevel);
}
#Override
public void start(Stage stage) throws Exception {
Button zoomIn = new Button("Zoom In");
zoomIn.setOnAction(event -> setZoomLevel(getZoomLevel() + 1));
zoomIn.setDisable(getZoomLevel() >= MAX_ZOOM_LEVEL);
zoomIn.disableProperty().bind(zoomLevel.greaterThanOrEqualTo(MAX_ZOOM_LEVEL));
Button zoomOut = new Button("Zoom Out");
zoomOut.setOnAction(event -> setZoomLevel(getZoomLevel() - 1));
zoomOut.setDisable(getZoomLevel() <= MIN_ZOOM_LEVEL);
zoomOut.disableProperty().bind(zoomLevel.lessThanOrEqualTo(MIN_ZOOM_LEVEL));
Label zoomLevelDescLabel = new Label("Zoom level (min " + MIN_ZOOM_LEVEL + ", max " + MAX_ZOOM_LEVEL + "): ");
Label zoomLevelValueLabel = new Label("" + getZoomLevel());
zoomLevelValueLabel.textProperty().bind(zoomLevel.asString());
Pane zoomLevelDisplay = new HBox(10, zoomLevelDescLabel, zoomLevelValueLabel);
Pane controlPane = new HBox(10, zoomIn, zoomOut);
Pane layout = new VBox(10, zoomLevelDisplay, controlPane);
layout.setPadding(new Insets(10));
stage.setScene(new Scene(layout));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have a GUI that takes the input of an image name, and once the refresh button is pressed, its displayed in a fixed position. I want to be able to input a character 'A-G' which will change the X position of the image, and a character '0-6' that will change the Y position of the image. The name of the images are just "A1", "A2"..."A5". So, if the user inputs "A1B3", it will display the image A1 in X-Position 'B' and Y-Position '3'. So B could be 200, and 3 could be 300, which makes the (X,Y) coordinates (200,300).
This is my code that gets the users input for the image.
private void getImage(){
Image img = new Image("comp1110/ass2/gui/assets/" + textField.getText() + ".png", 100, 100, false, false);
ImageView image = new ImageView();
image.setImage(img);
image.setX(100);
image.setY(100);
pane.getChildren().add(image);
}
I think you should do something along the lines of this yes there is some cosmetic issues that you will need to fix. But its only to give you an idea of what to do. It uses a gridpane so you don't have to worry about getting exact coordinates I choose a vbox so I didn't have to worry about layout you can keep the Pane that you have it shouldn't make a difference.
public class Main extends Application {
private GridPane gridPane;
private TextField imageTextField = new TextField();
private HashMap<String,String> hashMap = new HashMap<>();
#Override
public void start(Stage primaryStage) {
fillHashMapValues();
gridPane = new GridPane();
gridPane.setGridLinesVisible(true);
for (int i = 0; i < 7; i++) {
RowConstraints rowConstraints = new RowConstraints();
rowConstraints.setPercentHeight(14);
gridPane.getRowConstraints().add(rowConstraints);
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth(14);
gridPane.getColumnConstraints().add(columnConstraints);
gridPane.addColumn(i);
gridPane.addRow(i);
}
imageTextField.setPromptText("Enter Image Letters?");
TextField textField = new TextField();
textField.setPromptText("Enter Coordinates");
Button button = new Button("Go!");
button.setOnAction(event -> {
addToGridPane(textField.getText());
});
VBox vBox = new VBox();
vBox.setPrefSize(300, 300);
vBox.setAlignment(Pos.TOP_CENTER);
vBox.getChildren().addAll(imageTextField, textField, button, gridPane);
primaryStage.setScene(new Scene(vBox));
primaryStage.show();
button.requestFocus();//This is only so you can see the prompt text its irrelevant
}
private void fillHashMapValues(){
hashMap.put("A", "1");
hashMap.put("B", "2");
hashMap.put("C", "3");
hashMap.put("D", "4");
hashMap.put("E", "5");
hashMap.put("F", "6");
hashMap.put("G", "7");
}
private void addToGridPane(String string){
char[] chars = string.toCharArray();
if(chars.length==2){//Do more data validation here
if(hashMap.containsKey(String.valueOf(chars[0]))) {
int xValue = Integer.parseInt(hashMap.get(String.valueOf(chars[0])));
int yValue = Integer.parseInt(String.valueOf(chars[1]));
ImageView image = getImage();
gridPane.add(image, xValue, yValue);
}
}
}
private ImageView getImage(){
Image image = new Image("comp1110/ass2/gui/assets/" + imageTextField.getText() + ".png", 100, 100, false, false);
ImageView imageView = new ImageView();
imageView.setImage(image);
//imageView.setX(100);
//imageView.setY(100);
//pane.getChildren().add(image);
return imageView;
}
public static void main(String[] args) { launch(args); }
}
I'm trying to make TreeView with CheckBoxTreeItems. When I collapse/expand a CheckBoxTreeItems the image I set up does not display correctly. I googled but I couldn't find correct answer. On Stack Overflow, I found a similar problem, but I didn't get a valid answer.
E.g
JavaFX CheckBoxTreeItem graphic disappear when siblings collapse
JavaFX CheckBoxTreeItem: Graphic disappears if graph is extended
Any ideas?
public class ClientApplication extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(final Stage stage) {
ImageView folderIcon = new ImageView();
Image folderImage = new Image("image/folder.png");
folderIcon.setImage(folderImage);
folderIcon.setFitWidth(16);
folderIcon.setFitHeight(16);
CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("folder", folderIcon);
rootItem.setExpanded(true);
for (int i = 0; i < 4; i++) {
CheckBoxTreeItem<String> checkBoxTreeItem = new CheckBoxTreeItem<String>("Sample" + (i + 1), folderIcon);
rootItem.getChildren().add(checkBoxTreeItem);
}
final TreeView<String> tree = new TreeView<String>(rootItem);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
tree.setRoot(rootItem);
tree.setShowRoot(true);
StackPane root = new StackPane();
root.getChildren().add(tree);
stage.setScene(new Scene(root, 300, 250));
stage.show();
}
}
enter image description here
I tried to use the ideas provided by #Jai,But when I click the expand/collapse icon, there is still a problem.Attachment is a screenshot.Thanks in advance.
enter image description here
ImageView is a JavaFX control. This means that each instance represents a unique control you see on your screen. You should never use the same instance for multiple locations in your GUI.
On the other hand, Image represents an image (i.e. an array of pixel data), so it is reusable.
This should work:
#Override
public void start(final Stage stage) {
final Image folderImage = new Image("image/folder.png");
CheckBoxTreeItem<String> rootItem = new CheckBoxTreeItem<String>("folder", createImageView(folderImage));
rootItem.setExpanded(true);
for (int i = 0; i < 4; i++) {
CheckBoxTreeItem<String> checkBoxTreeItem = new CheckBoxTreeItem<String>("Sample" + (i + 1), createImageView(folderImage));
rootItem.getChildren().add(checkBoxTreeItem);
}
final TreeView<String> tree = new TreeView<String>(rootItem);
tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView());
tree.setRoot(rootItem);
tree.setShowRoot(true);
StackPane root = new StackPane();
root.getChildren().add(tree);
stage.setScene(new Scene(root, 300, 250));
stage.show();
}
private ImageView createImageView(Image folderImage) {
ImageView folderIcon = new ImageView();
folderIcon.setImage(folderImage);
folderIcon.setFitWidth(16);
folderIcon.setFitHeight(16);
return folderIcon;
}
I trying to build map which consist of regions(states) and when mouse entered to some region, I need handle it. Have many png images which represent each region separately. I blend my images and got what I want, but I can't handle some region.
For instance:
It's a first region img
This is a second region img
As result I got:
Code:
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("view/MapView.fxml"));
Pane root = loader.load();
primaryStage.setTitle("Map");
primaryStage.setScene(new Scene(root, 700, 700));
primaryStage.show();
//First region
File file = new File("src/res/img/region1.png");
Canvas canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 1"));
GraphicsContext graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
//Second region
file = new File("src/res/img/region2.png");
canvas = new Canvas(700, 700);
canvas.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, event -> System.out.println("Region 2"));
graphicsContext = canvas.getGraphicsContext2D();
graphicsContext.drawImage(new Image(file.toURI().toString()), 0, 0);
root.getChildren().add(canvas);
}
In console I got always "Region 2".
Please give me tips for research. Thanks in advance!
You can use an ImageView and setPickOnBounds for that.
Example code:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class LayersWithMouseEvents extends Application {
#Override
public void start(Stage primaryStage) {
// root
StackPane root = new StackPane();
// create layers
Pane region1Layer = new Pane();
Pane region2Layer = new Pane();
// add layers
root.getChildren().addAll(region1Layer, region2Layer);
// load images
ImageView region1ImageView = new ImageView( new Image( getClass().getResource("region1.png").toExternalForm()));
ImageView region2ImageView = new ImageView( new Image( getClass().getResource("region2.png").toExternalForm()));
// add images
region1Layer.getChildren().add(region1ImageView);
region2Layer.getChildren().add(region2ImageView);
// mouse handler
region1Layer.setOnMousePressed(e -> System.out.println("Region 1: " + e));
region2Layer.setOnMousePressed(e -> System.out.println("Region 2: " + e));
// this is the magic that allows you to click on the separate layers, but ONLY(!) as long as the layer is transparent
region1Layer.setPickOnBounds(false);
region2Layer.setPickOnBounds(false);
primaryStage.setScene(new Scene(root, 800, 600));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
In the future please create an MCVE with focus on complete. Nobody wants to waste their time in order to make your incomplete example work.
To my opinion using a canvas here is not the right approach. If you would define your regions as polygons and add them to the scene graph you can attach a listener to each of these polygons and then react accordingly if the mouse is over some region. Maybe this is also possible with image views but I have never tried wether a transparent region of an image is also mouse transparent which seems to be necessary in that case. For a program of mine I used polygons and it works nicely.
The image data can be used to determine the color of the pixels under the mouse cursor. If the pixel is not fully transparent, then the cursor is on that region.
To retrieve this information you need to use a listener for the mouse moved event. For simplicity you can use a property with a listener attached to trigger the region enter/leave events:
The following example assumes you keep references to the images used for the regions named image1 and image2:
PixelReader reader1 = image1.getPixelReader();
PixelReader reader2 = image2.getPixelReader();
SimpleIntegerProperty region = new SimpleIntegerProperty(-1);
region.addListener((observable, oldValue, newValue) -> {
if (newValue.intValue() < 0) {
System.out.println("region left");
} else {
System.out.println("Region " + (newValue.intValue() + 1));
}
});
canvas.setOnMouseMoved(event -> {
int x = (int) event.getX();
int y = (int) event.getY();
if (x < image1.getWidth() && y < image1.getHeight() && reader1.getColor(x, y).getOpacity() != 0) {
region.set(0);
} else if (x < image2.getWidth() && y < image2.getHeight() && reader2.getColor(x, y).getOpacity() != 0) {
region.set(1);
} else {
region.set(-1);
}
});
Also there is no need to create multiple Canvas to paint the images.
I am creating slot machine application with javafx. Desired behavior: separated pictures have to appear in pane as real slot machine drum during animation (pictures of cherry, lemon, number sever and etc should look like one whole peace for user) like showing on picture.
slot machine
My problem is I can't put together separate images to scrolling in slot machine window seamless.
I have made a lot of search about this problem but didn't find any working solutions. I have tried to add all images in ArrayList and then set them as node to TranslateTransition reference during animation process. But initial image stack in windows.
import javafx.animation.Interpolator;
import javafx.animation.ParallelTransition;
import javafx.animation.TranslateTransition;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TestClass extends Application {
private BorderPane borderPane = new BorderPane();
private GridPane gridPane = new GridPane();
#Override
public void start(Stage primaryStage) throws Exception {
ImageView image1 = new ImageView(createImage(Color.RED));
ImageView image2 = new ImageView(createImage(Color.GREEN));
ImageView image3 = new ImageView(createImage(Color.BLUE));
gridPane.setLayoutX(50);
gridPane.setLayoutY(50);
gridPane.setPadding(new Insets(5, 5, 5, 5));
gridPane.setAlignment(Pos.CENTER);
gridPane.setVgap(5);
gridPane.add(image1, 0, 0);
gridPane.add(image2, 1, 0);
gridPane.add(image3, 2, 0);
gridPane.setMaxWidth(image1.getFitWidth() * 3);
gridPane.setMaxHeight(image1.getFitHeight());
Rectangle clip = new Rectangle(732, 230);
clip.setLayoutX(30);
clip.setLayoutY(10);
clip.setStroke(Color.BLACK);
// clip.setFill(null);
gridPane.setClip(clip);
borderPane.setCenter(gridPane);
Scene scene = new Scene(borderPane, 900, 500);
primaryStage.setScene(scene);
primaryStage.setTitle("SlotMachine");
primaryStage.show();
ImageView[] images = { image1, image2, image3 };
TranslateTransition t1 = new TranslateTransition();
for (ImageView i : images) {
t1.setDuration(Duration.millis(2000));
t1.setNode(i);
t1.setFromX(image1.getX());
t1.setFromY(image1.getY() - gridPane.getHeight());
t1.setToX(image1.getX());
t1.setToY(image1.getY() - image1.getFitHeight() / 2 + gridPane.getHeight());
t1.setCycleCount(2);
t1.setAutoReverse(false);
t1.setInterpolator(Interpolator.LINEAR);
}
TranslateTransition t2 = new TranslateTransition();
for (ImageView i : images) {
t2.setDuration(Duration.millis(2000));
t2.setNode(i);
t2.setFromX(image2.getX());
t2.setFromY(image2.getY() - gridPane.getHeight());
t2.setToX(image2.getX());
t2.setToY(image2.getY() - image2.getFitHeight() / 2 + gridPane.getHeight());
t2.setCycleCount(2);
t2.setAutoReverse(false);
t2.setInterpolator(Interpolator.LINEAR);
}
TranslateTransition t3 = new TranslateTransition();
for (ImageView i : images) {
t3.setDuration(Duration.millis(2000));
t3.setNode(i);
t3.setFromX(image3.getX());
t3.setFromY(image3.getY() - gridPane.getHeight());
t3.setToX(image3.getX());
t3.setToY(image3.getY() - image3.getFitHeight() / 2 + gridPane.getHeight());
t3.setCycleCount(2);
t3.setAutoReverse(false);
t3.setInterpolator(Interpolator.LINEAR);
}
ParallelTransition pt = new ParallelTransition(t2, t3, t1);
pt.play();
}
private final Image createImage(Color color) {
Rectangle rect = new Rectangle(32, 32);
rect.setFill(color);
return rect.snapshot(null, null);
}
public static void main(String[] args) {
launch(args);
}
}
Please help. Thank you in advance
From what I can tell, it doesn't look like you are using Model/View/Controller architecture; using this architecture is the easiest way (in my opinion) to implement a gui like this. In your case, you could model your logic from the following psuedo-code:
In Controller:
//changes x or y coordinate (depending on direction of movement) of top left corner
//the controller should change the values of x or y that are stored in your model
moveImageMethod(image);
In View:
//Number of ms between timer events, play with this number to make it smooth
private static final int TIMER_INTERVAL = 1000/30;
//constructor
public View(){
<your other stuff, event handlers, etc.>
this.timer = new Timer(TIMER_INTERVAL, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
handleTimerTick();
}
});
}
// Start the animation timer.
public void startTimer() {
timer.start();
}
protected void handleTimerTick(){
controller.moveImageMethod(image);
<other stuff you might need/want>
repaint();
}
<your graphics methods>