Working in Java FX 2.2. The Scene has a horizontal width that is fixed but is unknown at compile time. I want to place 2 or more buttons in a horizontal row that completely fills the horizontal space in the Scene AND that are each exactly the same width. The number of buttons changes dynamically with the program state. What program snippet will accomplish this?
可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
回答1:
This code from the HBox javadoc will almost do what you want, except that "buttons themselves are different sizes based on the text contained in the button - wider text causes wider buttons".
HBox hbox = new HBox();
Button button1 = new Button("Add");
Button button2 = new Button("Remove");
HBox.setHgrow(button1, Priority.ALWAYS);
HBox.setHgrow(button2, Priority.ALWAYS);
button1.setMaxWidth(Double.MAX_VALUE);
button2.setMaxWidth(Double.MAX_VALUE);
hbox.getChildren().addAll(button1, button2);
By creating a custom layout pane based on HBox and overriding it's layout method, you can get exactly the behaviour you describe.
import javafx.application.Application;
import javafx.collections.ObservableList;
import javafx.event.*;
import javafx.scene.*;
import javafx.scene.control.Button;
import javafx.scene.layout.*;
import javafx.stage.Stage;
// displays equal width buttons which fill a layout region's width.
// http://stackoverflow.com/questions/12830402/javafx-2-buttons-size-fill-width-and-are-each-same-width
public class HorizontallyTiledButtons extends Application {
public static void main(String[] args) { launch(args); }
@Override public void start(Stage stage) {
final Button addButton = new Button("Add");
final Button removeButton = new Button("Remove");
final Button extraButton = new Button("The wizard of Frobozz is watching");
final ButtonBar buttonBar = new ButtonBar(5, addButton, removeButton);
addButton.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
buttonBar.addButton(extraButton);
}
});
removeButton.setOnAction(new EventHandler<ActionEvent>() {
@Override public void handle(ActionEvent event) {
buttonBar.removeButton(extraButton);
}
});
VBox layout = new VBox(10);
layout.getChildren().addAll(buttonBar);
layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
stage.setScene(new Scene(layout));
stage.setWidth(800);
stage.show();
}
class ButtonBar extends HBox {
ButtonBar(double spacing, Button... buttons) {
super(spacing);
getChildren().addAll(buttons);
for (Button b: buttons) {
HBox.setHgrow(b, Priority.ALWAYS);
b.setMaxWidth(Double.MAX_VALUE);
}
}
public void addButton(Button button) {
HBox.setHgrow(button, Priority.ALWAYS);
button.setMaxWidth(Double.MAX_VALUE);
ObservableList<Node> buttons = getChildren();
if (!buttons.contains(button)) {
buttons.add(button);
}
}
public void removeButton(Button button) {
getChildren().remove(button);
}
@Override protected void layoutChildren() {
double minPrefWidth = calculatePrefChildWidth();
for (Node n: getChildren()) {
if (n instanceof Button) {
((Button) n).setMinWidth(minPrefWidth);
}
}
super.layoutChildren();
}
private double calculatePrefChildWidth() {
double minPrefWidth = 0;
for (Node n: getChildren()) {
minPrefWidth = Math.max(minPrefWidth, n.prefWidth(-1));
}
return minPrefWidth;
}
}
}
Sample program output:
回答2:
A bit cleaner solution with property binding:
HBox buttonLayout = new HBox();
buttonLayout.getChildren().add(button1);
buttonLayout.getChildren().add(button2);
int btnCount = buttonLayout.getChildren().size();
button1.prefWidthProperty().bind(buttonLayout.widthProperty().divide(btnCount));
button2.prefWidthProperty().bind(buttonLayout.widthProperty().divide(btnCount));