In my JavaFX application I use a TreeView. The single TreeItems contain an image. Unfortunately, the image is shown only once.
The code for loading the image looks like the following. It is called every time the TreeView changes. The Images are cached in a Map ("icons"). Thus, a new ImageView is created every time.
public final ImageView getImage(final String imageConstant) {
final Image img;
if (icons.containsKey(imageConstant)) {
img = icons.get(imageConstant);
}
else {
if (isRunFromJAR) {
img = new Image("/path/" + imageConstant, iconSizeWidth,
iconSizeHeight, false, false);
}
else {
img = new Image(getClass().getResourceAsStream(imageConstant), iconSizeWidth,
iconSizeHeight, false, false);
}
icons.put(imageConstant, img);
}
return new ImageView(img);
In the updateItem method of my TreeCells, the ImageView returned above is stored into a field called icon. The field is used in the following code, also from updateItem:
if (icon == null) {
setGraphic(label);
}
else {
final HBox hbox = new HBox(ICON_SPACING);
final Label iconLabel = new Label("", icon);
hbox.getChildren().addAll(iconLabel, label);
setGraphic(hbox);
}
But for some reason, the problem shown in the gif occurs.
Update
Actually, the ImageViews were cached internally, which led to de-duplication. Using multiple ImageViews is the solution.
the ImageView returned above is stored into a field called icon.The field is used in the following code...
The ImageView is type of Node. A Node can only have one parent at a time!
So don't store/cache a ImageView(-Node). Create new ImageView-Nodes.
Edit:
As pointed out in the comments by James_D:
...you can use the same Image(-Object) for every ImageView...
Related
I am trying to get the images (blob) from DB and adding it to a tableview JavaFX
But the quality is drastically reducing, even color is inverted. Any solution for this?
My code :
Main Form
for (StudentDTO s : studentDTOS) {
ImageView img = ImageController.blobToImage(s.getImage());
Button btn = new Button("Edit");
tmList.add(new ReceptionistStudentTM(
s.getId(),
img,
s.getName(),
s.getEmail(),
s.getNic(),
s.getScholaMark(),
s.isStatus(),
btn
));
}
tblStudents.setItems(tmList);
ImageController.java
public class ImageController {
public static ImageView blobToImage(Blob blob) throws SQLException {
return new ImageView(new Image(blob.getBinaryStream()));
}
}
Thanks for your time
I have a class (TaskInOurProgram) which contains ImageView in constructer.
Here is a little code snipped - declaration and then constructer:
private ImageView closeTask;
private ImageView pauseTask;
private final Image pauseImage = new Image("file:images/pause.png");
private final Image closeImage = new Image("file:images/close.png");
public TaskInOurProgram(String name, String configName, String extensionOfTheFile) {
this.nameOfTheFile = name;
this.configName = configName;
this.extensionOfTheFile = extensionOfTheFile;
this.closeTask = new ImageView();
closeTask.setImage(closeImage);
this.pauseTask = new ImageView();
pauseTask.setImage(pauseImage);
}
Then I have a observableArrayList which contains many of these TaskInOurProgram objects.
private ObservableList<TaskInOurProgram> tasksInTheTableView = FXCollections.observableArrayList();
I have a tableView which displays this list of objects. So then it looks like this.
Object are not there from the beginning, they need to be added by clicking at button ADD TASK and because it is observableList, it will show this "tasks" immediately after adding.
WHAT I NEED: What I need is to create method inside of the controller which will do something after click on "pause" ImageView. I really do not know how to do it, I was thinking about listeners somehow. And before that I need to put click event on imageView.
Can you guys, please, help me with this? Thanks for any idea.
Try using a Button with a Graphic instead of a plain ImageView. That way you can use the Button.setOnAction() method.
Where you are currently defining your ImageView, change it to the following:
this.pauseTask = new Button("", new ImageView(pauseImage));
this.pauseTask.setOnAction(new EventHandler<ActionEvent>(){
#Override
public void handle(ActionEvent event) {
pause();
}
});
You can define a method somewhere else in your program, something like this:
private void pause() {
// your code here
}
Use setOnMouseClicked() method for imageView to handle click events.
pauseTask.setOnMouseClicked(new EventHandler<Event>() {
#Override
public void handle(Event event)
{
pauseTask();
}
});
Then you can handle it in other method
void pauseTask()
{
}
This question already has answers here:
Writing javafx.scene.image.Image to file?
(3 answers)
Closed 6 years ago.
I added effect and color adjustments to an image which is displayed with help of ImageView.
Now I want to save those changes to a different file. How can I perform this?
You need the snapshot functionality of the class Node paired with the fromFXImage method of SwingFXUtils.
Takes a snapshot of this node and returns the rendered image when it
is ready. CSS and layout processing will be done for the node, and any
of its children, prior to rendering it. The entire destination image
is cleared to the fill Paint specified by the SnapshotParameters.
Example:
ImageView imageViewAdjusted = new ImageView(new Image(getClass().getResource("thinking-man.jpg").toExternalForm(), 250, 250, true, true));
ColorAdjust colorAdjust = new ColorAdjust();
colorAdjust.setContrast(0.9);
imageViewAdjusted.setEffect(colorAdjust);
imageViewAdjusted.setCache(true);
imageViewAdjusted.setCacheHint(CacheHint.SPEED);
Button btnSave = new Button("Save to File");
btnSave.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
File outputFile = new File("D:/formattedPicture.png");
BufferedImage bImage = SwingFXUtils.fromFXImage(imageViewAdjusted.snapshot(null, null), null);
try {
ImageIO.write(bImage, "png", outputFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
Here is the method that's giving me trouble:
private static final String theURL = "/optimize/images/prun0.png";
public static void createChallenge() {
Stage challStage = new Stage();
Group challGroup = new Group();
Rectangle rect = new Rectangle(900, 500);
Image image = new Image(theURL);
System.out.println(image.getHeight()); //Prints 0.0
rect.setFill(new ImagePattern(image)); //Gives runtime error: "Image must be non-null"
challStage.setResizable(false);
challStage.setTitle("Optimize!");
//ImageView iv = new ImageView(image);
challGroup.getChildren().add(rect);
Scene challScene = new Scene(challGroup, 900, 500);
challStage.setScene(challScene);
challStage.setMinHeight(500);
challStage.setMinWidth(900);
challStage.show();
challStage.centerOnScreen();
challStage.setX(challStage.getX()-150);
}
As it says, it throws a runtime error about image being null. Am I missing something basic? Also, the ultimate goal here is to put an image onto a Stage or Scene. I tried to do it simply with adding ImageView objects to the defined challGroup, but the Stage came up blank. Due to this, I tried to add the image to a Rectangle object and there found the actual runtime error. I have thrice-checked the URL. I've also tried using "file:prun0.png" to similar results. Any advice is appreciated. Thanks to all.
I've looked everywhere online and have been stumped for a few hours now. I have a program where I have an array of 40 imageViews on a screen. What I want to happen is when I click on a particular imageView in the array, I want the image to change within the imageView.
Here is what I have:
public void initBubbles(){
Image image = new Image("file:src/bubbles/images/bubble.png");
for (int i = 0; i < bubbles.length; i++) {
//Creates a new bubble
bubbles[i] = new Bubble(image, 'A', 1);
//Creates a new image view
ivs[i] = new ImageView(image);
//Various lines of codes that put the imageView in the scene..... (Not relevant)
ivs[i].addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent event) {
System.out.println("Bubble Popped!");
ivs[i].setImage(new Image("popped.png":); //Will result in arrayOutOfBounds
}
});
}
}
The problem I am running into is getting the selected element from the MouseEvent. I understand that the line right after the "Bubble Popped!" message will cause an ArrayOutOfBounds exception. But I hope you understand what I am trying to. How can I get the imageView that was clicked based on the MouseEvent?
Any help is greatly appreciated as I've been stuck for a few hours.
I cannot check it in compiler now but based on the documentation I guess you will refer to the correct ImageView by the getTarget() method which MouseEvent inherits from javafx.event.Event.
So the correct code would be something like this:
#Override
public void handle(MouseEvent event) {
final ImageView iv = (ImageView) (event.getTarget());
iv.setImage(new Image("popped.png"));
}
And if you really wish to have a single EventHandler for entire array, you should define it outside of the for loop and assign it to a variable - otherwise you are creating a new instance of the handler for each ImageView.
public void initBubbles(){
final Image image = new Image("file:src/bubbles/images/bubble.png");
// Create an event handler to be re-used for all the ImageView's
final EventHandler eventHandler = new EventHandler<MouseEvent> () {
#Override
public void handle(MouseEvent event) {
final ImageView iv = (ImageView) (event.getTarget());
iv.setImage(new Image("popped.png"));
}
}
for (int i = 0; i < bubbles.length; i++) {
//Creates a new bubble
bubbles[i] = new Bubble(image, 'A', 1);
//Creates a new image view
ivs[i] = new ImageView(image);
// Register the event handler
ivs[i].addEventHandler(MouseEvent.MOUSE_CLICKED, eventHandler);
}
}
The handler code runs in different context than where you register the handler (addEventHandler()), so the value of i does not have the same meaning. You should consider the code of the handle() method as completely separate and independent on its surrounding code. The information of the "run-time context" of the handler is in the event which triggered the handler.
To use a parable, by calling the addEventHandler() you tell the ImageView this:
Dear ImageView, when a mouse event occurs above you, call the code of the handler.
And the handler:
Dear handler, when you are triggered, have a look at the target upon which you have been called, consider it as an instance of ImageView and set its image to something new.