为什么自定义控件不随其子控件调整大小?

如何解决为什么自定义控件不随其子控件调整大小?

考虑以下最小可重现示例:

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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?