Замена JavaFX 11 для column.impl_setWidth ()?

Я написал пользовательскую политику изменения ширины TableColumn. Вы можете увидеть его код в Java 8 / JavaFX 8 здесь, на 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:

Я также попытался использовать решение, представленное в этом ответе, для выполнения частного метода и получил то же исключение: "Есть методы с именем doSetWidth и параметрами [класс java.lang.Double] не найдены".


Попытка 5:

Я думал, что это сработает, но не тут-то было. Он выдал «java.lang.NoSuchFieldException: ширина». Однако, когда я перехожу к исходному коду TableColumnBase, я ясно вижу в строке 412 приватное поле, называемое шириной. Не уверен, почему это не удается.

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: невозможно сделать поле закрытым


Попытка 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


person JoshuaD    schedule 12.11.2018    source источник
comment
У меня нет исходных кодов OpenJFX 11, но я думаю, что попытка 5 должна использовать TableColumnBase.class.getDeclaredField("width"). Класс среды выполнения экземпляра column, вероятно, TableColumn, что не соответствует.   -  person Jai    schedule 12.11.2018
comment
Да, попытки 6 и 7 выясняют это. Но я сталкиваюсь с этим неприятным InaccessibleObjectException.   -  person JoshuaD    schedule 12.11.2018
comment
Попытки отражения не сработают из коробки из-за модульной системы. Поскольку ваш код, согласно ошибке, находится в безымянном модуле, вам нужно будет использовать --add-opens javafx.controls/javafx.scene.control=ALL-UNNAMED в командной строке.   -  person Slaw    schedule 12.11.2018
comment
Это остановило срабатывание исключений, но столбцы не меняют размер, как это было, когда я вызывал impl_setWidth().   -  person JoshuaD    schedule 12.11.2018
comment
Вместо этого вызовите TableColumnBase.doSetWidth(double) задумчиво. Предполагается, что это прямая замена impl_setWidth(double), за исключением того, что теперь он является частным пакетом.   -  person Jai    schedule 12.11.2018
comment
Да, пробовал, та же проблема - столбцы на самом деле не меняют размер. По крайней мере, не так, как их сделала impl_setWidth(). Я попытаюсь перейти к более простой тестовой среде, чтобы убедиться.   -  person JoshuaD    schedule 12.11.2018
comment
Хорошо, кажется, это работает в более простой настройке. Мне придется возиться с моим кодом ResizePolicy, чтобы понять, почему он там не работает. Спасибо!   -  person JoshuaD    schedule 12.11.2018
comment
На самом деле, это кажется идеальной заменой. У меня была опечатка, которая вызывала проблему в моем первом тесте. Спасибо @Jai!   -  person JoshuaD    schedule 12.11.2018


Ответы (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
person JoshuaD    schedule 12.11.2018
comment
Кстати, если вы когда-нибудь поместите свой код в модуль, замените ALL-UNNAMED на имя вашего модуля. - person Slaw; 12.11.2018
comment
Просто хочу отметить, что безопаснее использовать TableColumnBase.class, так как вы никогда не узнаете, когда кто-то расширится с TableColumn. - person Jai; 12.11.2018
comment
Также обратите внимание, что com.sun.javafx.scene.control.TableColumnBaseHelper.setWidth(TableColumnBase, double) может работать без использования отражений, хотя вам все равно придется использовать частный API. Обновление: похоже, он был разработан для этой цели. Хотя нашел это случайно. - person Jai; 12.11.2018
comment
Как бы я использовал этот частный API без отражения? - person JoshuaD; 12.11.2018
comment
Ну, вещи в com.sun.... считаются частным API. Он должен генерировать предупреждение, которое вам нужно будет подавить во время компиляции. Я все еще использую Java8/JavaFX8, поэтому я не уверен, что это все еще относится к OpenJFX11. - person Jai; 12.11.2018