I am struggling with intersections i need to .getShape() on a button.
But as the API states
When null, the Region is rendered as a rounded rectangle.
that means that by default button dont have shape set.
I don't want to .setShape() on the button but to check my intersections i need to .getShape() of the button which comes back null.
Is there a way to get the default shape of the button node while .getShape() returns null ?
I have 2 .observableArrayList() buttons and circles (kind of bubbles)
private final ObservableList<Circle> circles = FXCollections.observableArrayList();
private final ObservableList<Button> buttonsList = FXCollections.observableArrayList();
private final ObjectProperty<BoundsType> selectedBoundsType = new SimpleObjectProperty<>(BoundsType.BOUNDS_IN_PARENT);
Then I add all of my buttons and circles to obs lists:
buttonsList.addAll(rootPane.getChildrenUnmodifiable()
.stream()
.filter(node -> node instanceof Button)
.map(node -> (Button) node)
.collect(Collectors.toList()));
Circles are a bit different I add them separately with a mouse click.
and then check the intersections between circles and buttons.
for (Button btn : buttonsList) {
for (Circle c : circles) {
ShapePair pair = new ShapePair(c.getShape(), btn.getShape());
if (pair.intersects(selectedBoundsType.get())) {
System.out.println("Colision");
//logic
}
}
}
but like i said the btn.getShape() gets me null ;(
Right i think it just easier to do
for (Button btn : buttonsList) {
for (Circle c : circles) {
if (btn.getBoundsInParent().intersects(b.getBoundsInParent())) {
System.out.println("Colision");
}
}
}
Related
I'm trying to make a battleship game.
Here's a link to the code: Battleship
Basically, when you click on one of the ship buttons(battleship, cruiser, submarine, etc.) a Rectangle pops up on the screen. You can move this rectangle around the screen by clicking on it and dragging the mouse around. The class that adds all of the event handlers to the Rectangle can be found in class MouseGestures.java.
Event Handlers are added to the GridPane in the GameGUI.java class starting at line 82.
Basically, I'm trying to find the index of whatever node you drag this object over in the GridPane. I'm able to accomplish this without dragging, by applying setOnMouseEntered to each node in the gridpane, but when I try to utilize setOnMouseDragEntered, I get nothing.
Would anyone have an idea for how to get this to work?
Just to reiterate, I want to print in the console the index of each gridpane when I drag and move a node over the nodes contained in the gridpane.
These are the event handlers I have applied to the nodes in the gridpane.
for (int i=0; i<=11; i++) {
for (int j=0; j<=11; j++) {
int colIndex = i;
int rowIndex = j;
grid2[i][j] = new Rectangle();
grid2[i][j].setStroke(Color.BLACK);
grid2[i][j].setFill(null);
grid2[i][j].setStrokeWidth(1);
grid2[i][j].setWidth(30);
grid2[i][j].setHeight(30);
//Prints index of each grid2[][] upon mouse entry.
grid2[i][j].setOnMouseEntered( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
//Does not work! Want to print index of each grid2[][] upon mouse drag enter.
grid2[i][j].setOnMouseDragEntered( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
//Does not work! Want to print index of each grid2[][] upon mouse drag over.
grid2[i][j].setOnMouseDragOver( e -> {
System.out.printf("Mouse entered cell [%d, %d]%n", colIndex, rowIndex);
});
These are the event handlers for the object you create when you push one of the buttons.
private EventHandler<MouseEvent> onMousePressedEventHandler = event -> {
if (event.getSource() instanceof Rectangle) {
shipSelected = (Rectangle) (event.getSource());
dragContext.x = shipSelected.getTranslateX() - shipSelected.getWidth()/2;
dragContext.y = shipSelected.getTranslateY() - shipSelected.getHeight()/2;
}
};
private EventHandler<MouseEvent> onMouseDragDetectedEventHandler = event -> {
shipSelected.startFullDrag();
System.out.println("startfulldrag");
};
private EventHandler<MouseEvent> onMouseDraggedEventHandler = event -> {
if (event.getSource() instanceof Rectangle) {
shipSelected = (Rectangle) (event.getSource());
shipSelected.setX(dragContext.x + event.getX());
shipSelected.setY(dragContext.y + event.getY());
}
};
You need to make the Rectangle you move around mouse transparent with setMouseTransparent(true) while you are dragging it.
private EventHandler<MouseEvent> onMouseDragDetectedEventHandler = event -> {
shipSelected.startFullDrag();
shipSelected.setMouseTransparent(true)
System.out.println("startfulldrag");
};
Otherwise, the Rectangle is always the top-most element under your mouse pointer, and you will not get dragEntered events from the elements below (i.e. the grid).
Of course, do not forget to set mouse transparency back to false, when you ended dragging.
I have a ripple button that once it's clicked, plays a ripple effect animation.
Inside of the button's skin, there's a Circle whose opacity is set to 0 at the beginning of the execution of the program and once the button is clicked, the opacity is set to 1 and the radius of the button gets larger.
There is a DoubleBinding binding, whose definition is the following:
DoubleBinding circleRippleRadius = new DoubleBinding() {
{
bind(heightProperty(), widthProperty()); // Sets the parameters of the bind method.
}
#Override // Overrides the computeValue method, which computes the value of the binding.
protected double computeValue() {
return Math.max(heightProperty().get(), widthProperty().get()); // Return the greatest of both numbers
}
};
Instead of using the button's heightProperty and widthProperty properties, I'd like to use the height and width of the scene to which the button is added, since I want the circle that appears once the button is clicked to fill the entire screen.
How could I achieve this?
UPDATE: Here is the code that defines the animation's component's values and the animation itself:
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
private void createRippleEffect(Circle circleRipple) {
circleRipple.setOpacity(1.0); // Sets the opacity of the circleRipple to 0, since it must not be showed yet.
/*Fade Transition*/
FadeTransition fadeTransition = new FadeTransition(RIPPLE_DURATION, circleRipple);
fadeTransition.setInterpolator(Interpolator.EASE_OUT);
fadeTransition.setFromValue(1.0); // Sets the opacity to %100
fadeTransition.setToValue(1.0); // The opacity doesn't change.
/*Scale Transition*/
Timeline scaleRippleTimeline = new Timeline();
NumberBinding circleRippleRadius =
Bindings.max(Bindings.selectDouble(sceneProperty(), "width"),
Bindings.selectDouble(sceneProperty(), "height"));
circleRippleRadius.addListener((ObservableValue<? extends Number> observable, Number oldValue, Number newValue) -> { // Each time it changes
KeyValue scaleValue = new KeyValue(circleRipple.radiusProperty(), newValue, Interpolator.EASE_OUT);
KeyFrame scaleFrame = new KeyFrame(RIPPLE_DURATION, scaleValue);
scaleRippleTimeline.getKeyFrames().add(scaleFrame);
});
SequentialTransition rippleTransition = new SequentialTransition(); // The circle must change its opacity and scale at the same time
rippleTransition.getChildren().addAll(
scaleRippleTimeline,
fadeTransition
);
ParallelTransition parallelTransition = new ParallelTransition();
getStyleClass().addListener((ListChangeListener.Change<? extends String> c) -> { // Don't pay attention to this. The style changes if
// the CSS file has "toggle" or "flat" inside its list,
// but these are never added so it doesn't matter.
if (c.getList().indexOf("flat") == -1 && c.getList().indexOf("toggle") == -1) {
setMinWidth(88);
setEffect(new DropShadow(BlurType.GAUSSIAN, Color.rgb(0, 0, 0, 0.30), 5, 0.10, 0, 2));
parallelTransition.getChildren().addAll(rippleTransition); // parallelTransition is basically the same as rippleTransition, since
// "toggle" and "flat" are never added to the CSS's list.
} else {
parallelTransition.getChildren().addAll(rippleTransition);
setMinWidth(USE_COMPUTED_SIZE);
setEffect(null);
}
});
this.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
parallelTransition.stop(); // In case that the parallelTransition is already running, stop it.
circleRipple.setOpacity(0.0); // Sets the opacity of the circle to 0, since the animation must play from he beginning.
circleRipple.setRadius(0.1); // Sets the radius to 0.1 for the same reason as the circle's opacity.
circleRipple.setCenterX(event.getX()); // The center of the circle is the location in which the mouse was clicked.
circleRipple.setCenterY(event.getY()); // The center of the circle is the location in which the mouse was clicked.
parallelTransition.playFromStart(); // Plays the animation.
});
}
being this the method that defines the button's skin:
#Override
public Skin<?> createDefaultSkin() {
ButtonSkin buttonSkin = getButtonSkin();
if (buttonSkin == null) {
buttonSkin = new ButtonSkin(this);
Circle circleRipple = new Circle(0.1, RIPPLE_COLOR);
buttonSkin.getChildren().add(0, circleRipple);
setSkin(buttonSkin);
createRippleEffect(circleRipple);
getStyleClass().add("ripple-button"); // What the CSS does is changing the button's color and text size, nothing important.
}
return buttonSkin;
}
Thanks in advance.
SOLUTION:
The problem of the circleRippleRadius's value being shorter than what the stage's one was probably due to that the stage's size was different than the scene's. I didn't know that, now I do.
For having the button's circle filling the entire screen, all I had to do was passing the widthProperty and heightProperty properties of the stage as parameters through the button's constructor.
Inside of the button's class, I create two ReadOnlyDoubleProperty properties for the width and the height that are empty until a button is created; in which case, the values of the defined ReadOnlyDoubleProperty properties are overwritten by the value of the widthProperty and heightProperty properties passed as parameters.
Having done that, all what I have to do is adding a listener to each property and changing the value of the circleRippleRadius to the greater of the properties' values each time that one of them changes.
You should not be extending DoubleBinding. Instead, you should create bindings from the existing factory methods:
NumberBinding circleRippleRadius =
Bindings.max(widthProperty(), heightProperty());
// Optional
DoubleExpression circleRippleRadiusAsDouble =
DoubleExpression.doubleExpression(circleRippleRadius);
To bind to scene properties, you want to use Bindings.selectDouble, which can handle the initially null scene property of your button:
NumberBinding size =
Bindings.max(
Bindings.selectDouble(button.sceneProperty(), "width"),
Bindings.selectDouble(button.sceneProperty(), "height"));
I have the following code that works:
Button b = (Button) findViewById(R.id.my_button);
if(b != null) {
Rect bounds = new Rect();
parentLayout.getHitRect(bounds);
if(b.getLocalVisibleRect(bounds)) {
//it is visible, do code
}
}
Question: How can I change the code so that the I do what I am currently doing inside if(b.getLocalVisibleRect(bounds)) but when the middle of or the complete button has appeared?
With my code now, I can detect when the upper edge of the button has appeared. But it would be best if I was able to detect when almost all of the button has appeared.
Is there any way I can draw an arrow at the current position or add an arrow to a paragraph like a chunk to get something like:
This arrow points to the right -> thats it!
How can I draw "->" as arrow like in MS Word inline?
EDIT: if someone needs it:
public final Font SYMBOL_FONT = new Font(Font.getFamily(BaseFont.SYMBOL));
public Chunk getArrow(final boolean paramLeft) {
Chunk arrow;
if (paramLeft) {
arrow = new Chunk(String.valueOf((char)220), SYMBOL_FONT);
} else {
arrow = new Chunk(String.valueOf((char)222), SYMBOL_FONT);
}
return arrow;
}
Whenever I click a JSlider it gets positioned one majorTick in the direction of the click instead of jumping to the spot I actually click. (If slider is at point 47 and I click 5 it'll jump to 37 instead of 5). Is there any way to change this while using JSliders, or do I have to use another datastructure?
As bizarre as this might seem, it's actually the Look and Feel which controls this behaviour. Take a look at BasicSliderUI, the method that you need to override is scrollDueToClickInTrack(int).
In order to set the value of the JSlider to the nearest value to where the user clicked on the track, you'd need to do some fancy pants translation between the mouse coordinates from getMousePosition() to a valid track value, taking into account the position of the Component, it's orientation, size and distance between ticks etc. Luckily, BasicSliderUI gives us two handy functions to do this: valueForXPosition(int xPos) and valueForYPosition(int yPos):
JSlider slider = new JSlider(JSlider.HORIZONTAL);
slider.setUI(new MetalSliderUI() {
protected void scrollDueToClickInTrack(int direction) {
// this is the default behaviour, let's comment that out
//scrollByBlock(direction);
int value = slider.getValue();
if (slider.getOrientation() == JSlider.HORIZONTAL) {
value = this.valueForXPosition(slider.getMousePosition().x);
} else if (slider.getOrientation() == JSlider.VERTICAL) {
value = this.valueForYPosition(slider.getMousePosition().y);
}
slider.setValue(value);
}
});
This question is kind of old, but I just ran across this problem myself. This is my solution:
JSlider slider = new JSlider(/* your options here if desired */) {
{
MouseListener[] listeners = getMouseListeners();
for (MouseListener l : listeners)
removeMouseListener(l); // remove UI-installed TrackListener
final BasicSliderUI ui = (BasicSliderUI) getUI();
BasicSliderUI.TrackListener tl = ui.new TrackListener() {
// this is where we jump to absolute value of click
#Override public void mouseClicked(MouseEvent e) {
Point p = e.getPoint();
int value = ui.valueForXPosition(p.x);
setValue(value);
}
// disable check that will invoke scrollDueToClickInTrack
#Override public boolean shouldScroll(int dir) {
return false;
}
};
addMouseListener(tl);
}
};
This behavior is derived from OS. Are you sure you want to redefine it and confuse users? I don't think so. ;)