如何解决为什么自定义控件不随其子控件调整大小?
考虑以下最小可重现示例:
public class Launcher extends Application {
public static final double SCENE_SIZE = 500;
@Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.setGridLinesVisible(true);
grid.getColumnConstraints().setAll(
new ColumnConstraints(USE_COmpuTED_SIZE,USE_COmpuTED_SIZE,Priority.NEVER,HPos.LEFT,false),new ColumnConstraints(USE_COmpuTED_SIZE,Priority.ALWAYS,false)
);
grid.getRowConstraints().setAll(
new RowConstraints(USE_COmpuTED_SIZE,VPos.TOP,new RowConstraints(USE_COmpuTED_SIZE,false)
);
CustomControl customControl = new CustomControl("Lorem Ipsum ".repeat(10));
customControl.prefWidthproperty().bind(grid.widthproperty());
TextFlow textFlow = new TextFlow(new Text("Lorem Ipsum ".repeat(10)));
textFlow.prefWidthproperty().bind(grid.widthproperty());
grid.add(new Label("0"),0);
grid.add(customControl,1,0);
grid.add(new Label("1"),1);
grid.add(textFlow,1);
grid.add(new Label("2"),2);
grid.add(new Label("..."),2);
Scene scene = new Scene(grid,SCENE_SIZE,SCENE_SIZE);
primaryStage.setTitle("Demo");
primaryStage.setScene(scene);
primaryStage.setonCloseRequest(t -> Platform.exit());
primaryStage.show();
}
static class CustomControl extends Control {
private final StringProperty text = new SimpleStringproperty();
public CustomControl(String text) { setText(text); }
public String getText() { return text.get(); }
public StringProperty textproperty() { return text; }
public void setText(String text) { this.text.set(text); }
@Override
protected Skin<?> createDefaultSkin() { return new CustomControlSkin(this); }
}
static class CustomControlSkin extends SkinBase<CustomControl> {
TextFlow textFlow;
public CustomControlSkin(CustomControl control) {
super(control);
textFlow = new TextFlow();
textFlow.getChildren().setAll(new Text(control.getText()));
control.textproperty().addListener(
(obs,old,value) -> textFlow.getChildren().setAll(new Text(control.getText()))
);
control.heightproperty().addListener((observable,oldValue,newValue) -> System.out.println("control = " + newValue));
textFlow.heightproperty().addListener((observable,newValue) -> { System.out.println("textFlow = " + newValue); });
getChildren().setAll(textFlow);
}
}
}
调整窗口大小会触发 TextFlow 自动换行,因此 TextFlow 会更改其高度。问题是它不会导致 CustomControl 高度变化,它保持不变(注意控制台输出)。
与此同时,下一个网格行上的“纯”TextFlow 的行为与 CustomControl 所期望的完全相同。它会随着窗口大小的调整来回更改其高度(以及 GridPane 行高)。
我认为任何控件都会根据孩子的高度自动改变自己的高度(SkinBase 包含很多方法),但似乎我错了。简单地在 TextFlow 侦听器中设置控件高度无济于事,因为这样只能增加它。
更新:
好的,我找到了问题所在。发生这种情况是因为 TextFlow 需要实际宽度来计算其首选高度,但 SkinBase 使用 negative width value。解决方案:
control.widthproperty().addListener((observable,newValue) -> {
control.setPrefheight(textFlow.prefheight(newValue.doubleValue()));
});
更新 2:
基于@Slaw 的建议,这个也能正常工作。奇怪的是,width
arg 值总是 -1
,所以我们必须从 skinnable 中获取宽度值。
static class CustomControlSkin extends SkinBase<CustomControl> {
// ...
@Override
protected double computePrefheight(double width,double topInset,double rightInset,double bottomInset,double leftInset) {
return textFlow.prefheight(getSkinnable().getWidth() != 0 ? getSkinnable().getWidth() : -1);
}
@Override
protected double computeMaxHeight(double width,double leftInset) {
return computePrefheight(width,topInset,rightInset,bottomInset,leftInset);
}
}
解决方法
根据@Slaw 的建议,这里是一种可能的解决方案。奇怪的是,width
arg 值总是 -1
,所以我们必须从 skinnable 中获取宽度值。
static class CustomControlSkin extends SkinBase<CustomControl> {
// ...
@Override
protected double computePrefHeight(double width,double topInset,double rightInset,double bottomInset,double leftInset) {
return textFlow.prefHeight(getSkinnable().getWidth() != 0 ? getSkinnable().getWidth() : -1);
}
@Override
protected double computeMaxHeight(double width,double leftInset) {
return computePrefHeight(width,topInset,rightInset,bottomInset,leftInset);
}
}
每次控件宽度更改时,它都会调用 computeMaxHeight()
,它只是将计算委托给 SkinBase
。然后我们只是强制 Control 始终使用 TextFlow pref 高度。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。