So I have a StackPane. Its height is set to 300. Inside the stackpane is an imageview containing an image and a label. I set the image height to 250 and the alignment to top-center. I set the label alignment to bottom center of the pane. and yet every time I run the code the image is pushed to the bottom of the frame and the label text is on top of it so it is difficult to read. It does not matter if the stackpane's height is 500, the image is 100 Pos.TOP-CENTER, and the label is baseline-center, the text will not be displayed under the image like it is supposed to. instead it is displayed on the bottom of the image. can someone tell me why this is happening?
public class VBoxProductPane extends StackPane {
private MainController mainController;
private String name;
private Image image;
private String fileName;
private double price;
private int quantity;
private Button button = new Button();
private Product product;
public VBoxProductPane(Product product){
setPrefSize(250, 275);
this.name = product.getName();
this.price = product.getPrice();
this.quantity = product.getQuantity();
this.fileName = product.getFileName();
this.product = new Product(this.name, this.price, this.quantity, this.fileName);
setImage();
setButton();
setLabel();
setOnMouseEntered(e -> {
button.setVisible(true);
});
setOnMouseExited(e -> {
button.setVisible(false);
});
}
private void setButton(){
button.setText("Explore");
getChildren().add(button);
button.setVisible(false);
button.setOnAction(e -> {
button.setVisible(true);
});
button.setOnAction(e -> {
try {
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/ProductLayout.fxml"));
Stage secondaryStage = new Stage();
loader.setController(new ProductPage(mainController, product));
secondaryStage.setTitle(product.getName());
secondaryStage.setHeight(450);
secondaryStage.setWidth(600);
Scene scene = new Scene(loader.load());
secondaryStage.setScene(scene);
secondaryStage.show();
} catch (IOException ex) {
ex.printStackTrace();
}
});
}
private void setLabel(){
Label label = new Label(getLabelText());
setAlignment(label, Pos.BOTTOM_CENTER);
// this does nothing
label.setLayoutY(250);
label.setWrapText(true);
label.setTextAlignment(TextAlignment.CENTER);
getChildren().add(label);
setAlignment(label, Pos.BOTTOM_CENTER);
}
private String getLabelText(){
if (this.product.getName() == null){
System.out.println("name is null");
}
return this.product.getName();
}
private Image getImage(){
Image image = this.product.getImage();
return image;
}
private void setImage() {
ImageView imageViews = new ImageView();
imageViews.setImage(this.product.getImage());
setAlignment(imageViews, Pos.TOP_CENTER);
// this does nothing
imageViews.setFitHeight(150);
// does not matter what this height is set to
// image is always displayed at the bottom with text over top
imageViews.setFitWidth(250);
imageViews.setY(0);
getChildren().add(imageViews);
}
}
ImageView is a very simple type of Node. It is not resizable (it doesn't have min/max/pref values). This makes it (unfortunately) really akward to use in layouts.
The StackPane for example cannot make the correct decisions on what to do with the ImageView (as it doesn't have a preferred size, or even a maximum size) and just assigns it as much space as possible.
Things you can do to solve this:
1) Wrap the ImageView in a container and set its sizes (setting the maximum sizes to be the same as the fit sizes should work).
2) Use a VBox or BorderPane so you can place the Label at the bottom correctly.
3) Use setGraphic of Label to integrate the Image directly with the Label control.
Related
I am creating a simple JavaFX based game and when I add a Button to my Pane and try to perform some action on it by clicking the mouse button while hovering over the button nothing happens - as if it is disabled but still visible.
private class Game extends Parent {
public Game() {
private Button newGameButton;
private Scene secondScene;
private Stage secondStage;
private Pane pane;
VBox menu = new VBox(15);
VBox highScore = new VBox(15);
menu.setTranslateX(280);
menu.setTranslateY(250);
highScore.setTranslateX(100);
highScore.setTranslateY(200);
try {
onLoad();
} catch (IOException e) {
e.printStackTrace();
}
newGameButton = new MenuButtonView("New Game");
newGameButton.setOnMouseClicked(event -> {
pane = new Pane();
secondScene = new Scene(pane, 800, 600);
secondStage = new Stage();
secondStage.setScene(secondScene);
secondStage.show();
scoreAndCrackView();
mickeyView();
});
public void mickeyView(){
private Button leftDownRedButton;
leftDownRedButton = new Button();
rightDownRedButton.setTranslateX(640);
rightDownRedButton.setTranslateY(560);
pane.getChildren().add(rightDownRedButton);
leftDownRedButton.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
System.out.println("Clicked");
}
});
public void scoreAndCrackView()
{
vertBox = new VBox();
vertBox.setPrefSize(800, 100); // this one was causing issue, previously: vertBox.setPrefSize(800, 600)
vertBox.getChildren().addAll(scoreLabel,crackLabel);
pane.getChildren().add(vertBox);
}
}
UPDATE
Btw I found the cause of the error - as you guys suggested something was covering my buttons and more precisely the VBox in another method which was set to the whole screen. After changing the values and minimizing prefWidth and prefHeight I can click on the buttons. Lesson for the future to set the Box only for the required area.
I'm making a battle card game for an uni project (Hearthstone)
The "minion" cards on board have health and attack, and those are constantly changing, I'm trying to make a compound component that would be a center ImageIcon with two "squares" at the bottom left and right, representing the card's current Health and Attack, and one at the top left, representing its cost, all these as StringProperty
I'm really clueless on how to approach this, maybe a coumpound component isn't even necessary
This is an example of how a hearthstone card looks :
Use a StackPane. Put the image on the bottom, and then an AnchorPane on the top. Put a Text in each of the three cornerns, and bind the textProperty() of each to one of the StringProperties. Something like this:
public class BattleCard extends Application {
private static Label label;
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("Hello World!");
label = new Label();
label.setText("Waiting...");
StackPane root = new StackPane();
root.getChildren().add(new CardPane());
primaryStage.setScene(new Scene(root, 350, 420));
primaryStage.show();
}
public class CardPane extends StackPane {
public CardPane() {
ImageView cardImage = new ImageView(new Image("http://i.stack.imgur.com/1ljQH.png"));
AnchorPane anchorPane = new AnchorPane();
getChildren().addAll(cardImage, anchorPane);
Text costText = new Text("Cost");
Text healthText = new Text("Health");
Text attackText = new Text("Attack");
AnchorPane.setTopAnchor(costText, 0d);
AnchorPane.setLeftAnchor(costText, 0d);
AnchorPane.setBottomAnchor(healthText, 0d);
AnchorPane.setLeftAnchor(healthText, 0d);
AnchorPane.setBottomAnchor(attackText, 0d);
AnchorPane.setRightAnchor(attackText, 0d);
anchorPane.getChildren().addAll(costText, healthText, attackText);
}
}
}
In a JavaFX TableView, how can I
Create a multiline column?
Center its content?
And set background color for each (entire) line?
I managed to create a multiline column using a custom CellFactory. I'm also aware of setAlignment(Pos.CENTER) and setTextAlignment(TextAlignment.CENTER) to center text. However, the text in my sample app is not centered properly per line. Furthermore, I did not manage to set a background color on the Text objects. Now my approach is to add a Pane for each line, which works fine. But how do I make the Pane fill the column's entire width and 1/3rd of its height?
As a starting point, this is how I would expect the code to be (though, I'm aware it's not doing what I want):
multiCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
#Override public TableCell<Person, Person> call(TableColumn<Person, Person> multiCol) {
return new TableCell<Person, Person>() {
private Group grp = null;
#Override public void updateItem(final Person person, boolean empty) {
super.updateItem(person, empty);
this.setAlignment(Pos.CENTER);
if (!isEmpty()) {
Text text = new Text(person.getFirstName());
text.setX(0);
text.setY(0);
text.setTextAlignment(TextAlignment.CENTER); // Center text?
Pane pane = new Pane();
pane.setStyle("-fx-background-color: #66BB66;");
pane.setLayoutX(0);
pane.setLayoutY(0);
pane.setPrefHeight(20);
pane.setPrefWidth(this.prefWidth(-1)); // Column width?
// -----
Text text2 = new Text(person.getLastName());
text2.setX(0);
text2.setY(20);
text2.setTextAlignment(TextAlignment.CENTER); // Center text?
Pane pane2 = new Pane();
pane2.setStyle("-fx-background-color: #79A8D8;");
pane2.setLayoutX(0);
pane2.setLayoutY(20);
pane2.setPrefHeight(20);
pane2.setPrefWidth(this.prefWidth(-1)); // Column width?
// -----
Text text3 = new Text(person.getEmail());
text3.setX(0);
text3.setY(40);
text3.setTextAlignment(TextAlignment.CENTER); // Center text?
Pane pane3 = new Pane();
pane3.setStyle("-fx-background-color: #FF8888;");
pane3.setLayoutX(0);
pane3.setLayoutY(40);
pane3.setPrefHeight(20);
pane3.setPrefWidth(this.prefWidth(-1)); // Column width?
// -----
Group grp = new Group();
grp.getChildren().add(pane);
grp.getChildren().add(text);
grp.getChildren().add(pane2);
grp.getChildren().add(text2);
grp.getChildren().add(pane3);
grp.getChildren().add(text3);
setGraphic(grp);
setStyle("-fx-padding: 0 0 0 0;");
}
}
};
}
});
I'm expecting an output like this:
For a full, compilable code sample please check out this pastebin.
Use a suitable layout pane (e.g. a VBox), and add Labels to it. You can configure the labels to fill the width of a VBox using VBox.setHgrow(...). You also need to set the maximum width of the label to allow it to grow.
As an aside, it is not good practice to re-create the controls every time the updateItem(...) method is called. Create them once and then just configure them in the updateItem(...) method with the required data.
Example:
TableColumn<Person, Person> multiCol = new TableColumn<>("Multiline");
multiCol.setCellValueFactory(cellData ->
new ReadOnlyObjectWrapper<Person>(cellData.getValue()));
multiCol.setCellFactory(column -> new TableCell<Person, Person>() {
private VBox graphic ;
private Label firstNameLabel ;
private Label lastNameLabel ;
private Label emailLabel ;
// Anonymous constructor:
{
graphic = new VBox();
firstNameLabel = createLabel("#66BB66");
lastNameLabel = createLabel("#79A8D8");
emailLabel = createLabel("#FF8888");
graphic.getChildren().addAll(firstNameLabel,
lastNameLabel, emailLabel);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
}
private final Label createLabel(String color) {
Label label = new Label();
VBox.setVgrow(label, Priority.ALWAYS);
label.setMaxWidth(Double.MAX_VALUE);
label.setStyle("-fx-background-color: "+color+" ;");
label.setAlignment(Pos.CENTER);
return label ;
}
#Override
public void updateItem(Person person, boolean empty) {
if (person == null) {
setGraphic(null);
} else {
firstNameLabel.setText(person.getFirstName());
lastNameLabel.setText(person.getLastName());
emailLabel.setText(person.getEmail());
setGraphic(graphic);
}
}
});
I need to invert a Slider in javafx.
This is how I built the slider:
Slider slide = new Slider();
slide.setPrefHeight(height);
slide.setMin(0);
slide.setMax(100);
slide.setOrientation(javafx.geometry.Orientation.HORIZONTAL);
slide.setShowTickLabels(true);
slide.setShowTickMarks(true);
slide.setSnapToTicks(true);
This code creates a horizontally aligned slider from value 0 to 100.
But I would like to invert it. As in, place it horizontally but display values from 100 to 0. and not 0 to 100.
Any help? Thanks in advance.
One easy way to invert the slider, without actually doing it, is by using a custom label formatter. Then you just need to take the value and revert it too.
Something like this:
private final DecimalFormat df = new DecimalFormat( "#,##0.00" );
private final double MIN = 0d;
private final double MAX = 100d;
#Override
public void start(Stage primaryStage) {
VBox vbox=new VBox(20);
vbox.setPadding(new Insets(20));
Slider slide = new Slider();
slide.setPrefSize(300,100);
slide.setMin(MIN);
slide.setMax(MAX);
slide.setOrientation(javafx.geometry.Orientation.HORIZONTAL);
slide.setShowTickLabels(true);
slide.setShowTickMarks(true);
slide.setSnapToTicks(true);
slide.setLabelFormatter(new StringConverter<Double>(){
#Override
public String toString(Double object) {
return df.format(MAX-object+MIN);
}
#Override
public Double fromString(String string) {
throw new UnsupportedOperationException("Not supported yet.");
}
});
Label label = new Label("Value: ");
slide.valueProperty().addListener((ov,n,n1)->
label.setText("Value: "+(MAX-n1.doubleValue()+MIN)));
vbox.getChildren().addAll(slide, label);
Scene scene = new Scene(vbox, 400, 200);
primaryStage.setScene(scene);
primaryStage.show();
}
I have this JavaFX accordion which displays images:
public class Navigation {
private static final Image BLUE_FISH = new Image("/Blue-Fish-icon.png");
private static final Image RED_FISH = new Image("/Red-Fish-icon.png");
private static final Image YELLOW_FISH = new Image("/Yellow-Fish-icon.png");
private static final Image GREEN_FISH = new Image("/Green-Fish-icon.png");
public void initNavigation(Stage primaryStage, Group root, Scene scene) {
VBox stackedTitledPanes = createStackedTitledPanes();
ScrollPane scroll = makeScrollable(stackedTitledPanes);
scroll.getStyleClass().add("stacked-titled-panes-scroll-pane");
scroll.setPrefSize(395, 580);
scroll.setLayoutX(5);
scroll.setLayoutY(32);
//scene = new Scene(scroll);
root.getChildren().add(scroll);
}
private VBox createStackedTitledPanes() {
final VBox stackedTitledPanes = new VBox();
stackedTitledPanes.getChildren().setAll(
createTitledPane("Connections", GREEN_FISH),
createTitledPane("Tables", YELLOW_FISH),
createTitledPane("Description", RED_FISH),
createTitledPane("Blue Fish", BLUE_FISH));
((TitledPane) stackedTitledPanes.getChildren().get(0)).setExpanded(true);
stackedTitledPanes.getStyleClass().add("stacked-titled-panes");
return stackedTitledPanes;
}
public TitledPane createTitledPane(String title, Image... images) {
FlowPane content = new FlowPane();
for (Image image : images) {
ImageView imageView = new ImageView(image);
content.getChildren().add(imageView);
FlowPane.setMargin(imageView, new Insets(10));
}
content.setAlignment(Pos.TOP_CENTER);
TitledPane pane = new TitledPane(title, content);
pane.getStyleClass().add("stacked-titled-pane");
pane.setExpanded(false);
return pane;
}
private ScrollPane makeScrollable(final VBox node) {
final ScrollPane scroll = new ScrollPane();
scroll.setContent(node);
scroll.viewportBoundsProperty().addListener(new ChangeListener<Bounds>() {
#Override
public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds) {
node.setPrefWidth(bounds.getWidth());
}
});
return scroll;
}
}
I'm interested is it possible to display rows of data where the images are placed. Something like this:
P.S case example. I have a java object which will be used as list:
public List<dataObj> list = new ArrayList<>();
public class dataObj {
private int connectionId;
private String conenctionname;
private String connectionDescription;
public dataObj() {
}
....................
}
When I insert some data into the Java Array list I want to display it into the accordion based on the above requirement.
P.S 2 In my case what is the proper way to insert text into FlowPane? I tested this:
public TitledPane createTitledPane(String title, Image... images) {
FlowPane content = new FlowPane();
for (Image image : images) {
ImageView imageView = new ImageView(image);
content.getChildren().add(imageView);
FlowPane.setMargin(imageView, new Insets(10));
}
content.setAlignment(Pos.TOP_CENTER);
content.setText("This part will be the first line.\n This part the second.");
TitledPane pane = new TitledPane(title, content);
pane.getStyleClass().add("stacked-titled-pane");
pane.setExpanded(false);
return pane;
}
I get error that inserting text using setText is not correct. What is the proper way?
If you use "\n" the output String will be separated into multiple lines of text.
For example:
component.setText("This part will be the first line.\n This part the second.");
From your update, assuming you have getters and setters:
component.setText(String.valueOf(dataObj.getConnectionId()) + "\n" + dataObj.getConnectionname() + "\n" + dataObj.getConnectionDescription());
You can simply use a ListView:
private void hello() {
ListView<Object> lv = new ListView<>();
// yourList is you List<Object> list
lv.itemsProperty().set(yourList);
lv.setCellFactory(new Callback<ListView<Object>, ListCell<Object>>() {
#Override
public ListCell<Object> call(ListView<Object> p) {
return new youCellFactory();
}
});
AnchorPane content = new AnchorPane();
content.getChildren().add(lv);
// add to TitelPane
TitledPane pane = new TitledPane(title, content);
}
static class youCellFactory extends ListCell<Object> {
#Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
setText(item.getConenctionname());
}
}
}
I have not tested this code but it should work.
Here is an nice Example too, but without object:
ListViewSample.java