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.
Related
I have an RCP view with a GeoMap widget inside it. When the view is created, the map is centred on a position.
The problem I'm having is that when GeoMap calls getSize() it is wrong. It is returning (0,0).
I've found one method of fixing this but it required changing code in GeoMap even though I believe the problem is to do with the code in the view. If I change the setCenterPosition method for GeoMap to use the parents client area it works.
View
#PostConstruct
public void createPartControl(final Composite parent) {
Display.getDefault().asyncExec(() -> setInitialPosition());
}
private void setInitialPosition() {
widget.setCenterPosition(new PointD(longitude, latitude));
widget.redraw();
}
GeoMap
public void setCenterPosition(final Point p) {
// `parent.getClientArea()` instead of `getSize()`
final Rectangle size = getParent().getClientArea();
setMapPosition(p.x - (size.width / 2), p.y - (size.height / 2));
}
The problem with this is that I've had to change code in GeoMap to fix a problem that is likely to do with getting the size of the view. The view hasn't had the size set by the time I call to centre the map.
I've also tried adding a resize listener to the view. This wouldn't always produce the same results when getting the size of the GeoMap widget.
parent.addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(final org.eclipse.swt.widgets.Event e) {
if (firstResize) {
setInitialPosition();
firstResize = false;
}
}
});
Here mentions that a resize listener should be used, and getClientArea. The problem is that GeoMap doesn't use getClientArea but I don't know if it should be.
Is this a problem with how I'm calling setCenterPosition or a bug in GeoMap?
Update
I've also tried adding a resize listener to the widget.
widget.addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(final org.eclipse.swt.widgets.Event e) {
if (firstResize) {
setInitialPosition();
firstResize = false;
}
}
});
For some reason the size the widget gets is too small. It is getting a size of (615, 279) when it should be (768, 388).
By adding a button to call setCenterPosition again it gets the correct size of (768, 388).
I can fix this if I add it inside an async block but this seems more like a workaround than an actual fix.
widget.addListener(SWT.Resize, new Listener() {
#Override
public void handleEvent(final org.eclipse.swt.widgets.Event e) {
if (firstResize) {
Display.getDefault().asyncExec(() -> {
setInitialPosition();
firstResize = false;
});
}
}
});
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()
{
}
The idea of the system is when a user clicks a texture it will move to the icon which is an ImageButton.
I have a method transitionTextureToActor().
If a user clicks the texture this method is called.
This method just changes properties of the Image so this Image will duplicate a texture and it moves to the icon which is ImageButton. After action of movement and sizing is completed the Image is supposed to become invisible.
private void transitionTextureToActor(ShapeProcessor shapeProcessor, Image icon) {
tempActor = shapeProcessor.getAnimatedImage();
tempActor.setSize(shapeProcessor.getWidth(), shapeProcessor.getHeight());
tempActor.setPosition(shapeProcessor.getX(), shapeProcessor.getY());
tempActor.setVisible(true);
shapeProcessor.setVisible(false);
tempActor.addAction(
sequence(
parallel(
sizeTo(icon.getDrawable().getMinWidth(), icon.getDrawable().getMinHeight(), .3F),
moveTo(icon.getX(), icon.getY(), .3f)
),
run(new Runnable() {
#Override
public void run() {
tempActor.setVisible(false);
}
})
)
);
}
So it works well for a single call of transitionTextureToActor method.
After resizing and movement the image disappears.
But when I call several objects of ShapeProcessor at the same time only first one disappears.
for(ShapeProcessor shape: shapes){
transitionTextureToActor(shape);
}
I want all of them to be invisible. Must be something wrong with RunnableAction because there the image becomes invisible but I can't work out what.
That's because at the time, run() methods of RunnableActions are called, they all point at the same object, referenced by tempActor. Try creating local references and pass them to RunnableActions:
private void transitionTextureToActor(ShapeProcessor shapeProcessor, Image iconClicked) {
tempActor = shapeProcessor.getAnimatedImage();
tempActor.setSize(shapeProcessor.getWidth(), shapeProcessor.getHeight());
tempActor.setPosition(shapeProcessor.getX(), shapeProcessor.getY());
tempActor.setVisible(true);
shapeProcessor.setVisible(false);
final Actor localReference = tempActor;
tempActor.addAction(
sequence(
parallel(
sizeTo(iconClicked.getDrawable().getMinWidth(), iconClicked.getDrawable().getMinHeight(), .3F),
moveTo(iconClicked.getX(), iconClicked.getY(), .3f)
),
run(new Runnable() {
#Override
public void run() {
localReference.setVisible(false);
}
})
)
);
}
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...
When trying to put a simple ImageButton on stage, It didn't seem to detect clicks.
ImageButton btnStart = new ImageButton(ButtonArt.UP, ButtonArt.DOWN));
// btnStart.setClickListener(new ClickListener() {
// #Override
// public void click(Actor a, float arg1, float arg2) {
// a.visible = false;
// }
// });
stage.addActor(btnStart);
ButtonArt.UP and ButtonArt.DOWN are TextureRegions, of each state.
Now when I click on the button, it doesn't change state! I also tried the above ClickListener (for testing), but it seemed that didn't work either.
In my render method I just call stage.act() and stage.render().
I also tried drawing the TextureRegions with SpriteBatch in my render method, and they are in fact different textures.
Am I doing something wrong?
You will need to set the stage as your inputprocessor:
Gdx.input.setInputProcessor(stage);
If you need to have multiple inputprocessors (e.g., you need clicks registered outside your scene), you will need to use an InputMultiplexer, like this:
InputMultiplexer plex = new InputMultiplexer();
plex.addProcessor(myOtherProcessor);
plex.addProcessor(stage);
Gdx.input.setInputProcessor(plex);