【问题标题】:JavaFX 11 Replacement for column.impl_setWidth()?JavaFX 11 替换 column.impl_setWidth()?
【发布时间】:2019-04-14 18:41:30
【问题描述】:

我写了一个自定义的 TableColumn 宽度调整策略。在Java下可以看到它的代码 8 / JavaFX 8 here on github。如您所见,在distributeSpaceRatio() 等方法中,我们的计算后,它依赖于TableColumn.impl_setWidth() 将列的宽度设置为所需的值。

我正在将我的项目迁移到 Java 11 / JavaFX 11,并且方法 impl_setWidth() 已从公共视图中删除。

是否有另一种方法可以告诉表格列设置其宽度?我愿意接受任何解决方案,只要它是可靠的并将 with 设置为(或接近)请求的值。

到目前为止,我尝试了两个没有成功的想法:


尝试1:

这个给NoMethodFoundException:

private void setColumnWidth ( TableColumn column, double targetWidth ) {

    try {
        Method method = column.getClass().getDeclaredMethod( "setWidth" );
        method.setAccessible( true );
        Object r = method.invoke( targetWidth );
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}

尝试2:

这个没有效果:

private void setColumnWidth ( TableColumn column, double targetWidth ) {

    double oldMax = column.getMaxWidth();
    double oldMin = column.getMinWidth();
    double oldPref = column.getPrefWidth();

    column.setMaxWidth( targetWidth );
    column.setMinWidth( targetWidth );
    column.setPrefWidth( targetWidth );

    column.getTableView().refresh();

    column.setPrefWidth( oldPref );
    column.setMinWidth( oldMin );
    column.setMaxWidth( oldMax );
}

尝试3:

查看 JavaFX 的 TableView.UNCONSTRAINED_RESIZE_POLICY 的源代码,似乎调用的方法最终是 TableColumnBase.doSetWidth(),但这对我来说也不起作用:

private void setColumnWidth ( TableColumn column, double targetWidth ) {

    try {
        Method method = column.getClass().getDeclaredMethod( "doSetWidth" );
        method.setAccessible( true );
        Object r = method.invoke( targetWidth );
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}

尝试4:

我还尝试使用this answer 中提供的解决方案来执行私有方法,并得到相同的异常——“找不到名称为 doSetWidth 和参数 [class java.lang.Double] 的方法”。


尝试 5:

我以为这个会起作用,但没有这样的运气。它抛出了“java.lang.NoSuchFieldException:width”。但是,当我转到 TableColumnBase 源代码时,我在第 412 行清楚地看到了一个名为 width 的私有字段。不知道为什么这会失败。

private void setColumnWidth ( TableColumnBase column, double targetWidth ) {
    try {
        Field widthField = column.getClass().getDeclaredField( "width" );
        widthField.setAccessible( true );
        ReadOnlyDoubleWrapper width = (ReadOnlyDoubleWrapper)widthField.get( column );
        width.set( targetWidth );
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}

尝试 6:

这让我们更接近了,但仍然失败:

private void setColumnWidth ( TableColumnBase column, double targetWidth ) {
    try {
        Field widthField = column.getClass().getSuperclass().getDeclaredField( "width" );
        widthField.setAccessible( true );
        ReadOnlyDoubleWrapper width = (ReadOnlyDoubleWrapper)widthField.get( column );
        width.set( targetWidth );
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}

java.lang.reflect.InaccessibleObjectException:无法将字段设为私有 javafx.beans.property.ReadOnlyDoubleWrapper javafx.scene.control.TableColumnBase.width 可访问:模块 javafx.controls 不会“打开 javafx.scene.control”以未命名模块@649dcffc


尝试 7:

private void setColumnWidth ( TableColumn column, double targetWidth ) {
    try {
        Method method = column.getClass().getSuperclass().getDeclaredMethod( "setWidth", double.class );
        method.setAccessible( true );
        method.invoke ( targetWidth );
        //ReadOnlyDoubleWrapper width = (ReadOnlyDoubleWrapper)widthField.get( column );
        //width.set( targetWidth );
    } catch ( Exception e ) {
        e.printStackTrace();
    }
}

java.lang.reflect.InaccessibleObjectException:无法使 void javafx.scene.control.TableColumnBase.setWidth(double) 可访问:模块 javafx.controls 不会“打开 javafx.scene.control”到未命名模块 @5063f647

【问题讨论】:

  • 我没有 OpenJFX 11 源代码,但我认为尝试 5 应该使用 TableColumnBase.class.getDeclaredField("width")column 实例的运行时类可能是 TableColumn,不会匹配。
  • 是的,尝试 6 和 7 解决了这个问题。但我遇到了那个讨厌的“InaccessibleObjectException”。
  • 由于模块系统的原因,反射的尝试不会开箱即用。由于您的代码是,根据错误,在“未命名模块”中,您需要在命令行中使用--add-opens javafx.controls/javafx.scene.control=ALL-UNNAMED
  • 这阻止了异常的触发,但列的大小并没有像我调用 impl_setWidth() 时那样改变。
  • 反省地致电TableColumnBase.doSetWidth(double)。这应该是impl_setWidth(double) 的直接替换,只是它现在是包私有的。

标签: java javafx java-11 javafx-11


【解决方案1】:

基于测试和其他人的帮助,我找到了这个解决方案:

private void setColumnWidth ( TableColumn column, double targetWidth ) {
    try {
        Method method = TableColumnBase.class.getDeclaredMethod( "doSetWidth", double.class );
        method.setAccessible( true );
        method.invoke( column, targetWidth );
    } catch ( NoSuchMethodException | InvocationTargetException | IllegalAccessException e ) {
        e.printStackTrace();
    }
}

此外,需要使用以下参数调用 JVM:

--add-opens javafx.controls/javafx.scene.control=ALL-UNNAMED

【讨论】:

  • 顺便说一句,如果你曾将代码放入模块中,请将 ALL-UNNAMED 替换为你的模块名称。
  • 只想指出使用TableColumnBase.class 更安全,因为您永远不知道何时有人从TableColumn 扩展。
  • 另请注意,com.sun.javafx.scene.control.TableColumnBaseHelper.setWidth(TableColumnBase, double) 可能在不使用反射的情况下工作,但您仍然必须使用私有 API。更新:看起来它是为此目的而设计的。不过是偶然发现的。
  • 如何在不反射的情况下使用该私有 API?
  • 好吧,com.sun.... 中的内容被视为私有 API。它应该生成一个警告,您需要在编译时禁止该警告。我仍在使用 Java8/JavaFX8,所以我不确定 OpenJFX11 是否仍然如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-26
  • 2014-12-13
相关资源
最近更新 更多