【发布时间】:2020-06-22 14:47:46
【问题描述】:
我不得不使用 ToggleButtons 而不是 Tabs 将 TabPane 切换为 Toolbar。这是因为与开箱即用的 Tab 相比,我需要更好地控制“选项卡”的内容和图形以及拖放功能。我可以让 Tab 表现出我想要的样子,但是因为我需要在 Tab 的图形中的标签上设置我的文本(在我的例子中是 HBox),这意味着溢出的 ContextMenu 没有任何可见的文本。我还希望我的选项卡中的其他布局属性可以在 ContextMent 项目中继承,而工具栏很好地为我提供了这一点(背景颜色和位于“选项卡”文本旁边的图像。 我遇到的问题是显示在选项卡上的图像,现在是 ToggleButtons(我的代码中的 FakeTabs),是动态更新的,因此会更改 ToggleButton 的宽度。如果我在 FakeTab 中设置以下内容:
//set minSize to prevent Label text being truncated.
`this.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);`
以便图像旁边的标签上的文本不会被截断,工具栏不会调整大小,并且用于打开 ContextMenu 的 >> 按钮最终会覆盖最后一个 ToggleButton,但它也会错过一些 ToggleButtons菜单,因此您无法访问它们。 有没有办法设置工具栏,以便在更改时将其调整为孩子的宽度,或者我可以强制重新布局?当我说调整大小时,我希望它保持它的实际宽度,但重新计算哪些 ToggleButtons 需要位于 ContextMenu 中,并保持最右边的 >> 按钮不超过 ToggleButton。 我在 ToolBar 上尝试了各种 .requestLayout() 方法,但到目前为止没有成功。
我的 reprex 添加了一些我的 FakeTabs (ToggleButtons),然后在应用程序运行后将逐渐添加一些 Circles(替换我的应用程序中添加的图像)。这会导致 ToggleButtons 变宽并且 ToolBar 无法调整大小以适应孩子们的新宽度的问题。 删除
this.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
从 FakeTab 导致 FakeTab 的文本在添加圆圈时被截断,这对我来说不可行。
感谢您的帮助。
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.StrokeLineCap;
import javafx.scene.shape.StrokeType;
import javafx.stage.Stage;
import java.util.Arrays;
import java.util.List;
public class MainApp extends Application {
private CustomTabPane customTabPane;
@Override
public void start(Stage stage) {
customTabPane = new CustomTabPane();
VBox.setVgrow(customTabPane, Priority.ALWAYS);
VBox vbox = new VBox(2.0);
vbox.getChildren().setAll(customTabPane);
BorderPane root = new BorderPane();
root.setMaxWidth(800);
root.setMaxHeight(400);
root.setCenter(vbox);
//add some fake tabs
List<DataModel> dataModels = Arrays.asList(
new DataModel("Tab1 name ", new int[]{0, 0, 255}),
new DataModel("Tab2 name 22", new int[]{0, 0, 140}),
new DataModel("Tab3 name 333", new int[]{0, 0, 100}),
new DataModel("Tab4 name 4444", new int[]{255, 0, 0}),
new DataModel("Tab5 name 55555", new int[]{100, 0, 0}),
new DataModel("Tab6 name 666666", new int[]{0, 255, 0}),
new DataModel("Tab7 name 7777777", new int[]{0, 140, 0}),
new DataModel("Tab8 name 88888888", new int[]{0, 100, 0}),
new DataModel("Tab9 name 999999999", new int[]{50, 0, 50}),
new DataModel("Tab10 name xxxxxxxxxx", new int[]{110, 0, 50}));
for (DataModel data : dataModels) {
addFakeTab(data);
}
//simulated Images being updated to Tabs after the CustomTabPane has been populated with FakeTabs.
Task task = new Task<Void>() {
@Override
public Void call() throws InterruptedException {
Thread.sleep(3000);
for (int i = 0; i < 3; i++) {
Platform.runLater(() -> {
updateTabIcons();
});
Thread.sleep(3000);
}
return null;
}
};
new Thread(task).start();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("CustomTabPane");
stage.show();
}
public static void main(String [] args){
launch(args);
}
/**
* Fake updating FakeTabs with 'images'.
*/
private void updateTabIcons() {
for (int i = 0; i < customTabPane.getToolBarNodes().size(); i++) {
Node node = customTabPane.getToolBarNodes().get(i);
if (node instanceof FakeTab) {
((FakeTab) node).addImage(createCircle());
}
}
}
/**
* Add a circle where the image would go
**/
private Circle createCircle() {
Circle circle = new Circle(10);
circle.setFill(Color.BLACK);
circle.setStrokeType(StrokeType.INSIDE);
circle.setStrokeWidth(2.0);
circle.setStrokeLineCap(StrokeLineCap.BUTT);
circle.setStroke(Color.WHITE);
return circle;
}
private void addFakeTab(DataModel data) {
FakeTab fakeTab = new FakeTab(data);
customTabPane.addTab(fakeTab);
}
class CustomTabPane extends VBox {
private StackPane stackPane;
private ToolBar toolBar;
public CustomTabPane() {
toolBar = new ToolBar();
stackPane = new StackPane();
this.setAlignment(Pos.TOP_CENTER);
this.getChildren().setAll(toolBar, stackPane);
}
public ObservableList<Node> getToolBarNodes() {
return toolBar.getItems();
}
public void addTab(ToggleButton toggleButton) {
toolBar.getItems().add(toggleButton);
}
}
class DataModel {
private String id;
private int[] rgbColor;
public DataModel(String id, int[] rgbColor) {
this.id = id;
this.rgbColor = rgbColor;
}
public int[] getRgbColor() {
return rgbColor;
}
public String getRgbString() {
return "" + rgbColor[0] + "," + rgbColor[1] + "," + rgbColor[2];
}
public String getStringId() {
return id;
}
}
}
FakeTab 类:
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
public class FakeTab extends ToggleButton {
private Label label;
private HBox imageHBox;
private MainApp.DataModel dataModel;
public FakeTab(MainApp.DataModel dataModel) {
super("");
this.dataModel = dataModel;
//set minSize to prevent Label text being truncated.
this.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
label = new Label();
imageHBox=new HBox(1.0);
HBox hBox = new HBox(imageHBox,label);
hBox.setStyle("-fx-padding: 1.0em;");
hBox.setAlignment(Pos.CENTER_LEFT);
this.setGraphic(hBox);
this.setUserData("" + dataModel.getStringId());
label.setText("" + dataModel.getStringId());
int[] rgb = dataModel.getRgbColor();
Color color = getFXColor(rgb[0], rgb[1], rgb[2], 1.0);
String style = "-fx-background-color:rgb(" + dataModel.getRgbString() + ");-fx-text-fill:" + (isColorDark(color) ? "white" : "black") + ";";
String style2 = "-fx-background-color:rgb(" + dataModel.getRgbString() + ");-fx-text-fill:transparent;";
this.setStyle(style2);
label.setStyle(style);
}
//add a Circle to simulate the effect adding images has.
public void addImage(Circle circle){
imageHBox.getChildren().add(circle);
}
private Color getFXColor(int iR, int iG, int iB, double opacity) {
double r = ((double) iR) / 255;
double g = ((double) iG) / 255;
double b = ((double) iB) / 255;
return new Color(r, g, b, opacity);
}
private boolean isColorDark(Color color) {
boolean isDark = false;
double boundary = 0.4980392156862745;
int colorChk = 0;
if (color.getRed() > boundary) {
colorChk += 1;
}
if (color.getGreen() > boundary) {
colorChk += 2;
}
if (color.getBlue() > boundary) {
colorChk += 4;
}
switch (colorChk) {
case 0:
case 1:
case 4:
isDark = true;
break;
}
return isDark;
}
}
【问题讨论】:
-
看看 TabPaneSkin 的源代码,它应该在弹出窗口显示时处理该问题 - 您可以从中获得复制的想法:)