用 JavaFx 中的选项卡填充所有 TabPane 宽度
Fill all the TabPane width with tabs in JavaFx
在JavaFx中,我做了一个TabPane这个样子:
如你所见,space右边有一个空白,留给箭头按钮,以防TabPane太短。我想完全填充 TabPane 宽度,没有空白。
这是我的代码:
HelloApplication.java:
package com.example.demo;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
HelloController.java:
package com.example.demo;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import java.net.URL;
import java.util.ResourceBundle;
public class HelloController implements Initializable {
private static final int GAP = 19;
@FXML
private TabPane tabPane = null;
@FXML
private BorderPane borderPane = null;
@Override
public void initialize(URL location, ResourceBundle resources) {
// Permits to change the width of the tabs to fit all the space.
tabPane.tabMinWidthProperty()
.bind(tabPane.widthProperty()
.divide(tabPane.getTabs()
.size())
.subtract(GAP));
}
}
你好-view.css:
.tab-header-area {
-fx-padding: 0 0 0 0;
}
.tab-header-background {
-fx-background-color: transparent;
}
.tab-down-button {
-fx-padding: -7;
}
.tab-down-button .arrow {
-fx-padding: -7;
}
.tab {
-fx-background-color: transparent;
-fx-border-width: 0 0 0 0;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-focus-color: transparent;
-fx-faint-focus-color: transparent;
}
.tab-label {
-fx-font-size: 13px;
-fx-font-weight: bold;
}
.tab:hover {
-fx-background-color: cyan;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
.tab:pressed {
-fx-background-color: gray;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
.tab:selected {
-fx-background-color: blue;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
你好-view.fxml:
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import java.net.URL?>
<TabPane fx:id="tabPane" side="BOTTOM" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.HelloController">
<tabs>
<Tab fx:id="trackTab" text="Tracks">
<content>
<BorderPane fx:id="borderPane"/>
</content>
</Tab>
<Tab text="Volumes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" />
</content>
</Tab>
</tabs>
<stylesheets>
<URL value="@hello-view.css" />
</stylesheets>
</TabPane>
在控制器中,我使用绑定来设置 tabPane 宽度,在 CSS 中,我尝试使箭头 space 消失。我也想获得 GAP 常量的 RID。你有解决办法吗?
潜在方法
三种可能的解决方案:
使用 CSS.
以您想要的方式设置现有选项卡的样式
- 如您所见,这很难。
- 如果默认选项卡样式和皮肤实现在未来版本中发生变化,它也会很脆弱。
创建一个新的 TabPaneSkin 并关联 CSS。
- 这可能不那么脆弱,因为现在您有了自己的皮肤实现。
- 然而,现有的 TabPaneSkin 实现非常复杂,即使是微不足道的定制也非常非常困难。
实施您自己的自定义布局、控件和 CSS 以管理切换窗格。
- 这非常稳定,因为您只依赖基本的标准 public 控件,例如按钮和布局窗格。
- 这是非常可定制的,因为您从空白开始,然后添加您想要的功能。
- TabPane 控件在菜单、拖动选项卡、添加选项卡、动画选项卡、键盘输入支持等方面具有 很多 的 in-built 功能。
- 在自定义实现中,您将失去所有这些附加功能。
- 但是,对于许多应用程序,您可能不需要或不需要这些附加功能。
- 如果您确实需要额外的功能,请使用前两种方法中的任何一种,否则我建议您使用这种方法。
自定义窗格开关实现
结构
VBox(contentPane, controlPane)
contentPane 包含您的可切换窗格并设置为:
VBox.setVgrow(contentPane, Priority.ALWAYS);
controlPane提供标签切换按钮:
HBox(new RadioButton("Tracks"), new RadioButton("Volumes"));
操作单选按钮时,contentPane 将替换为该按钮的相应窗格。
使用单选按钮而不是切换按钮,因此,当将切换组分配给按钮时,一次只能选择一个按钮。
单选按钮的 radio-button
样式已被删除,样式类似于切换按钮(通过 CSS),因此它们看起来更像标准按钮。
示例代码
此示例内联 CSS 而不是提供单独的文件,它还使用了 fx:root
结构。如果您愿意,您可以有一个单独的 CSS 文件而不使用 fx:root
结构。
fx:root 和内联 CSS 构造缺少一些有用的工具支持。如果不使用这些功能,您将在场景生成器中获得更好的所见即所得视图,并在 IDE.
中改进智能编辑
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mixer</artifactId>
<version>1.0-SNAPSHOT</version>
<name>mixer</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>17.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>17.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
module-info.java
module com.example.mixer {
requires javafx.controls;
requires javafx.fxml;
opens com.example.mixer to javafx.fxml;
exports com.example.mixer;
}
MixerApp.java
package com.example.mixer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MixerApp extends Application {
@Override
public void start(Stage stage) {
stage.setScene(new Scene(new Mixer()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Mixer.java
package com.example.mixer;
import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import java.io.IOException;
import java.util.Map;
public class Mixer extends VBox {
private final Map<Toggle, Pane> paneMap;
private static final String CSS = """
data:text/css,
.mixer {
tracks-color: honeydew;
volumes-color: lemonchiffon;
}
.tracks-pane {
-fx-background-color: tracks-color;
-fx-font-size: 20px;
}
.volumes-pane {
-fx-background-color: volumes-color;
-fx-font-size: 20px;
}
.tracks-pane-selector {
-fx-base: tracks-color;
-fx-font-size: 16px;
}
.volumes-pane-selector {
-fx-base: volumes-color;
-fx-font-size: 16px;
}
""";
public Mixer() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mixer.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
getStylesheets().add(CSS);
// we want the pane selectors styled as toggle-buttons rather than radio-buttons,
// so we remove their radio styles.
tracksPaneSelector.getStyleClass().remove("radio-button");
volumesPaneSelector.getStyleClass().remove("radio-button");
StackPane tracksPane = new StackPane(new Label("Tracks"));
tracksPane.getStyleClass().add("tracks-pane");
StackPane volumesPane = new StackPane(new Label("Volumes"));
volumesPane.getStyleClass().add("volumes-pane");
paneMap = Map.of(
tracksPaneSelector, tracksPane,
volumesPaneSelector, volumesPane
);
displaySelectedPane();
paneToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) ->
displaySelectedPane()
);
}
private void displaySelectedPane() {
contentPane.getChildren().setAll(
paneMap.get(paneToggleGroup.getSelectedToggle())
);
}
// FXML fields generated from skeleton.
@FXML
private StackPane contentPane;
@FXML
private HBox paneControls;
@FXML
private ToggleGroup paneToggleGroup;
@FXML
private RadioButton tracksPaneSelector;
@FXML
private RadioButton volumesPaneSelector;
}
mixer.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<fx:root fx:id="mixerLayout" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" styleClass="mixer" type="VBox" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane fx:id="contentPane" VBox.vgrow="ALWAYS" />
<HBox fx:id="paneControls">
<children>
<RadioButton fx:id="tracksPaneSelector" maxWidth="1.7976931348623157E308" mnemonicParsing="false" selected="true" text="Tracks" HBox.hgrow="SOMETIMES">
<toggleGroup>
<ToggleGroup fx:id="paneToggleGroup" />
</toggleGroup>
<styleClass>
<String fx:value="toggle-button" />
<String fx:value="tracks-pane-selector" />
</styleClass>
</RadioButton>
<RadioButton fx:id="volumesPaneSelector" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Volumes" toggleGroup="$paneToggleGroup" HBox.hgrow="SOMETIMES">
<styleClass>
<String fx:value="toggle-button" />
<String fx:value="volumes-pane-selector" />
</styleClass>
</RadioButton>
</children>
</HBox>
</children>
</fx:root>
在JavaFx中,我做了一个TabPane这个样子:
如你所见,space右边有一个空白,留给箭头按钮,以防TabPane太短。我想完全填充 TabPane 宽度,没有空白。
这是我的代码: HelloApplication.java:
package com.example.demo;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch();
}
}
HelloController.java:
package com.example.demo;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TabPane;
import javafx.scene.layout.BorderPane;
import java.net.URL;
import java.util.ResourceBundle;
public class HelloController implements Initializable {
private static final int GAP = 19;
@FXML
private TabPane tabPane = null;
@FXML
private BorderPane borderPane = null;
@Override
public void initialize(URL location, ResourceBundle resources) {
// Permits to change the width of the tabs to fit all the space.
tabPane.tabMinWidthProperty()
.bind(tabPane.widthProperty()
.divide(tabPane.getTabs()
.size())
.subtract(GAP));
}
}
你好-view.css:
.tab-header-area {
-fx-padding: 0 0 0 0;
}
.tab-header-background {
-fx-background-color: transparent;
}
.tab-down-button {
-fx-padding: -7;
}
.tab-down-button .arrow {
-fx-padding: -7;
}
.tab {
-fx-background-color: transparent;
-fx-border-width: 0 0 0 0;
-fx-background-radius: 0;
-fx-background-insets: 0;
-fx-focus-color: transparent;
-fx-faint-focus-color: transparent;
}
.tab-label {
-fx-font-size: 13px;
-fx-font-weight: bold;
}
.tab:hover {
-fx-background-color: cyan;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
.tab:pressed {
-fx-background-color: gray;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
.tab:selected {
-fx-background-color: blue;
-fx-border-color: black;
-fx-border-width: 0 0 2 0;
}
你好-view.fxml:
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import java.net.URL?>
<TabPane fx:id="tabPane" side="BOTTOM" tabClosingPolicy="UNAVAILABLE" xmlns="http://javafx.com/javafx/null" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demo.HelloController">
<tabs>
<Tab fx:id="trackTab" text="Tracks">
<content>
<BorderPane fx:id="borderPane"/>
</content>
</Tab>
<Tab text="Volumes">
<content>
<AnchorPane minHeight="0.0" minWidth="0.0" />
</content>
</Tab>
</tabs>
<stylesheets>
<URL value="@hello-view.css" />
</stylesheets>
</TabPane>
在控制器中,我使用绑定来设置 tabPane 宽度,在 CSS 中,我尝试使箭头 space 消失。我也想获得 GAP 常量的 RID。你有解决办法吗?
潜在方法
三种可能的解决方案:
使用 CSS.
以您想要的方式设置现有选项卡的样式- 如您所见,这很难。
- 如果默认选项卡样式和皮肤实现在未来版本中发生变化,它也会很脆弱。
创建一个新的 TabPaneSkin 并关联 CSS。
- 这可能不那么脆弱,因为现在您有了自己的皮肤实现。
- 然而,现有的 TabPaneSkin 实现非常复杂,即使是微不足道的定制也非常非常困难。
实施您自己的自定义布局、控件和 CSS 以管理切换窗格。
- 这非常稳定,因为您只依赖基本的标准 public 控件,例如按钮和布局窗格。
- 这是非常可定制的,因为您从空白开始,然后添加您想要的功能。
- TabPane 控件在菜单、拖动选项卡、添加选项卡、动画选项卡、键盘输入支持等方面具有 很多 的 in-built 功能。
- 在自定义实现中,您将失去所有这些附加功能。
- 但是,对于许多应用程序,您可能不需要或不需要这些附加功能。
- 如果您确实需要额外的功能,请使用前两种方法中的任何一种,否则我建议您使用这种方法。
自定义窗格开关实现
结构
VBox(contentPane, controlPane)
contentPane 包含您的可切换窗格并设置为:
VBox.setVgrow(contentPane, Priority.ALWAYS);
controlPane提供标签切换按钮:
HBox(new RadioButton("Tracks"), new RadioButton("Volumes"));
操作单选按钮时,contentPane 将替换为该按钮的相应窗格。
使用单选按钮而不是切换按钮,因此,当将切换组分配给按钮时,一次只能选择一个按钮。
单选按钮的 radio-button
样式已被删除,样式类似于切换按钮(通过 CSS),因此它们看起来更像标准按钮。
示例代码
此示例内联 CSS 而不是提供单独的文件,它还使用了 fx:root
结构。如果您愿意,您可以有一个单独的 CSS 文件而不使用 fx:root
结构。
fx:root 和内联 CSS 构造缺少一些有用的工具支持。如果不使用这些功能,您将在场景生成器中获得更好的所见即所得视图,并在 IDE.
中改进智能编辑pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>mixer</artifactId>
<version>1.0-SNAPSHOT</version>
<name>mixer</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>17.0.2</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>17.0.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
module-info.java
module com.example.mixer {
requires javafx.controls;
requires javafx.fxml;
opens com.example.mixer to javafx.fxml;
exports com.example.mixer;
}
MixerApp.java
package com.example.mixer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MixerApp extends Application {
@Override
public void start(Stage stage) {
stage.setScene(new Scene(new Mixer()));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Mixer.java
package com.example.mixer;
import javafx.fxml.*;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import java.io.IOException;
import java.util.Map;
public class Mixer extends VBox {
private final Map<Toggle, Pane> paneMap;
private static final String CSS = """
data:text/css,
.mixer {
tracks-color: honeydew;
volumes-color: lemonchiffon;
}
.tracks-pane {
-fx-background-color: tracks-color;
-fx-font-size: 20px;
}
.volumes-pane {
-fx-background-color: volumes-color;
-fx-font-size: 20px;
}
.tracks-pane-selector {
-fx-base: tracks-color;
-fx-font-size: 16px;
}
.volumes-pane-selector {
-fx-base: volumes-color;
-fx-font-size: 16px;
}
""";
public Mixer() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mixer.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
getStylesheets().add(CSS);
// we want the pane selectors styled as toggle-buttons rather than radio-buttons,
// so we remove their radio styles.
tracksPaneSelector.getStyleClass().remove("radio-button");
volumesPaneSelector.getStyleClass().remove("radio-button");
StackPane tracksPane = new StackPane(new Label("Tracks"));
tracksPane.getStyleClass().add("tracks-pane");
StackPane volumesPane = new StackPane(new Label("Volumes"));
volumesPane.getStyleClass().add("volumes-pane");
paneMap = Map.of(
tracksPaneSelector, tracksPane,
volumesPaneSelector, volumesPane
);
displaySelectedPane();
paneToggleGroup.selectedToggleProperty().addListener((observable, oldValue, newValue) ->
displaySelectedPane()
);
}
private void displaySelectedPane() {
contentPane.getChildren().setAll(
paneMap.get(paneToggleGroup.getSelectedToggle())
);
}
// FXML fields generated from skeleton.
@FXML
private StackPane contentPane;
@FXML
private HBox paneControls;
@FXML
private ToggleGroup paneToggleGroup;
@FXML
private RadioButton tracksPaneSelector;
@FXML
private RadioButton volumesPaneSelector;
}
mixer.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<fx:root fx:id="mixerLayout" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="150.0" prefWidth="300.0" styleClass="mixer" type="VBox" xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1">
<children>
<StackPane fx:id="contentPane" VBox.vgrow="ALWAYS" />
<HBox fx:id="paneControls">
<children>
<RadioButton fx:id="tracksPaneSelector" maxWidth="1.7976931348623157E308" mnemonicParsing="false" selected="true" text="Tracks" HBox.hgrow="SOMETIMES">
<toggleGroup>
<ToggleGroup fx:id="paneToggleGroup" />
</toggleGroup>
<styleClass>
<String fx:value="toggle-button" />
<String fx:value="tracks-pane-selector" />
</styleClass>
</RadioButton>
<RadioButton fx:id="volumesPaneSelector" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Volumes" toggleGroup="$paneToggleGroup" HBox.hgrow="SOMETIMES">
<styleClass>
<String fx:value="toggle-button" />
<String fx:value="volumes-pane-selector" />
</styleClass>
</RadioButton>
</children>
</HBox>
</children>
</fx:root>