JavaFX工具栏调整到子对象的动态宽度
我不得不用ToggleButtons而不是Tabs来切换TabPane作为工具栏。这是因为我需要对“Tab”的内容和图形以及拖放功能进行更好的控制,而不是像Tab那样开箱即用。我可以让选项卡按我想要的方式运行和显示,但因为我需要在选项卡图形的标签上设置文本(在我的例子中是HBox),这意味着溢出上下文菜单没有任何可见文本。我还希望我的标签页的其他布局属性可以在上下文项中继承,工具栏为我提供了很好的布局属性(背景颜色和图像布置在标签文本旁边)。 我遇到的问题是,选项卡上显示的图像(现在是ToggleButtons(代码中的FakeTabs))会动态更新,因此会更改ToggleButton的宽度。如果我在FakeTab中设置以下内容:JavaFX工具栏调整到子对象的动态宽度,java,javafx,javafx-11,Java,Javafx,Javafx 11,我不得不用ToggleButtons而不是Tabs来切换TabPane作为工具栏。这是因为我需要对“Tab”的内容和图形以及拖放功能进行更好的控制,而不是像Tab那样开箱即用。我可以让选项卡按我想要的方式运行和显示,但因为我需要在选项卡图形的标签上设置文本(在我的例子中是HBox),这意味着溢出上下文菜单没有任何可见文本。我还希望我的标签页的其他布局属性可以在上下文项中继承,工具栏为我提供了很好的布局属性(背景颜色和图像布置在标签文本旁边)。 我遇到的问题是,选项卡上显示的图像(现在是Toggl
//设置minSize以防止标签文本被截断。
`this.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);`
为了使图像旁边标签上的文本不会被截断,工具栏不会调整大小,打开ContextMenu的>>按钮结束于最后一个ToggleButton,但它也会错过菜单中的一些ToggleButton,因此您无法访问它们。
有没有一种方法可以设置工具栏,使其在更改时可以调整到其子对象的宽度,或者我可以强制重新显示?当我说“调整大小”时,我希望它保持其实际宽度,但重新计算哪些切换按钮需要位于ContextMenu中,并将>>按钮保持在最右侧,而不是切换按钮上方。
我尝试了各种方法来打开工具栏上的.requestLayout(),但到目前为止没有成功
我的reprex添加了一些faketab(ToggleButtons),然后在应用程序运行后,会逐渐添加一些圆圈(替换我应用程序中添加的图像)。这会导致ToggleButtons变宽,工具栏无法调整大小以适应孩子们的新宽度。
移除
this.setMinSize(Button.USE_PREF_SIZE,Button.USE_PREF_SIZE);
From 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;
}
}
}
我已经浏览了ToolBarSkin的源代码,因为它以我需要的方式处理溢出(渲染切换按钮的背景色,包括子项,如图像)。它有一个私有布尔值needsUpdate,设置为在删除或添加项目时尝试,但不会在儿童项目的维度发生变化时尝试。目前,我使用的hack是访问此私有字段的反射,因此我可以在请求工具栏重新显示其子项之前将其设置为true。它满足我的需要,但我不希望有e使用反射
//use reflection to access private member field needsUpdate. This is the boolean
//used by the skin to trigger a relayout of the ToolBar when items are added/removed.
//we need to use this to force a relayout when children change sizes too.
Field f1 = ToolBarSkin.class.getDeclaredField("needsUpdate");
f1.setAccessible(true);
f1.setBoolean(skin, true);
this.requestLayout();
感谢您的评论。看看TabPaneSkin的源代码,当弹出窗口显示时,它应该会处理这个问题-您可以从以下位置获得复制的想法:)
//use reflection to access private member field needsUpdate. This is the boolean
//used by the skin to trigger a relayout of the ToolBar when items are added/removed.
//we need to use this to force a relayout when children change sizes too.
Field f1 = ToolBarSkin.class.getDeclaredField("needsUpdate");
f1.setAccessible(true);
f1.setBoolean(skin, true);
this.requestLayout();