Как я могу обойти JavaFX TableView «заполнитель»?

JavaFX-хTableView имеетзаполнитель свойство, которое в основномNode который отображается вTableView всякий раз, когда он пуст. Если для этого свойства установлено значение null (значение по умолчанию), оно отображается какLabel или другой текстовыйNode что говорит "В таблице нет содержимого. "

Но если в таблице есть какие-либо строки данных, то заполнительNode исчезает, и все вертикальное пространство вTableView заполняется строками, в том числе пустыми, если нетt достаточно данных, чтобы заполнить всю таблицу.

Эти пустые строки - то, что я хочу,даже когда стол пуст, Другими словами, я неЯ не хочу использовать заполнитель вообще. Кто-нибудь знает, как я могу это сделать?

я бы не стал делать что-то клуджое, например, пустую строку вTableView всякий раз, когда этодолжно быть на самом деле пустым.

 jewelsea18 окт. 2013 г., 08:29
 SnakeDoc27 сент. 2013 г., 03:35
+1 яя ищу способ сделать это тоже. В кодировании, вы можете вручную установить placeHolder, ямы обычно устанавливаем это к изображению:table.setPlaceholder(new ImageView(new Image(new FileInputStream("someImage.png")))); Но я'я хотел бы, чтобы он просто отображал пустые строки ... также был бы удобен способ сделать это в FXML
 kleopatra25 янв. 2018 г., 13:37
координаты ошибки в openjdk rep:bugs.openjdk.java.net/browse/JDK-8090949 - все еще не решена в fx9 / 10

Ответы на вопрос(5)

старая проблема все еще не исправлено ни в fx9, ни в fx10. Так что пересмотрел хаки в контексте fx9. Произошли изменения, хорошие и плохие:

Скины перемещены в общедоступный пакет, который теперь позволяет создавать их подклассы без доступа к скрытым классам (хорошо)Движениевнес ошибку который неt позволяет установить пользовательский VirtualFlow (исправлено в fx10)Отражающий доступ к скрытым членам будет категорически запрещен (читай: невозможно) когда-нибудь в будущем

Во время копания я заметил очень небольшие глюки с хаки (примечание: я не запускал их против fx8, так что это может быть из-за различий в fx8 против fx9!)

Принудительная невидимость / видимость заполнителя / потока работала нормально, за исключением случаев, когда запуск с пустой таблицы (был показан заполнитель) и увеличение таблицы в то время как пустой (новый» регион выглядит пустым)Фальсификация itemCount в not-empty позволяет исчезать строкам при нажатии клавиш навигации (что, вероятно, не является большой проблемой, поскольку пользователи не склонны перемещаться по пустой таблице) - это определенно введено в fx9, отлично работает в fx8

Поэтому я решил пойти на усиление видимости: причина небольших глюков в том, что layoutChildren немакет потока, если он думает, что заполнитель виден. Тот'обрабатывается путем включения потока в макет, если супер не сделалт.

Пользовательский скин:

/**
 * TableViewSkin that doesn't show the placeholder.
 * The basic trick is keep the placeholder/flow in-/visible at all 
 * times (similar to https://stackoverflow.com/a/27543830/203657).
 * <p> 
 * 
 * Updated for fx9 plus ensure to update the layout of the flow as
 * needed.
 * 
 * @author Jeanette Winzenburg, Berlin
 */
public class NoPlaceHolderTableViewSkin<t> extends TableViewSkin<t>{

    private VirtualFlow<!--?--> flowAlias;
    private TableHeaderRow headerAlias;
    private Parent placeholderRegionAlias;
    private ChangeListener<boolean> visibleListener = (src, ov, nv) -> visibleChanged(nv);
    private ListChangeListener<node> childrenListener = c -> childrenChanged(c);

    /**
     * Instantiates the skin.
     * @param table the table to skin.
     */
    public NoPlaceHolderTableViewSkin(TableView<t> table) {
        super(table);
        flowAlias = (VirtualFlow<!--?-->) table.lookup(".virtual-flow");
        headerAlias = (TableHeaderRow) table.lookup(".column-header-background");

        // startet with a not-empty list, placeholder not yet instantiatet
        // so add alistener to the children until it will be added
        if (!installPlaceholderRegion(getChildren())) {
            installChildrenListener();
        }
    }


    /**
     * Searches the given list for a Parent with style class "placeholder" and
     * wires its visibility handling if found.
     * @param addedSubList
     * @return true if placeholder found and installed, false otherwise.
     */
    protected boolean installPlaceholderRegion(
            List<!--? extends Node--> addedSubList) {
        if (placeholderRegionAlias !=  null) 
            throw new IllegalStateException("placeholder must not be installed more than once");
        List<node> parents = addedSubList.stream()
                .filter(e -> e.getStyleClass().contains("placeholder"))
                .collect(Collectors.toList());
        if (!parents.isEmpty()) {
            placeholderRegionAlias = (Parent) parents.get(0);
            placeholderRegionAlias.visibleProperty().addListener(visibleListener);
            visibleChanged(true);
            return true;
        }
        return false;
    }


    protected void visibleChanged(Boolean nv) {
        if (nv) {
            flowAlias.setVisible(true);
            placeholderRegionAlias.setVisible(false);
        }
    }


    /**
     * Layout of flow unconditionally.
     * 
     */
    protected void layoutFlow(double x, double y, double width,
            double height) {
        // super didn't layout the flow if empty- do it now
        final double baselineOffset = getSkinnable().getLayoutBounds().getHeight() / 2;
        double headerHeight = headerAlias.getHeight();
        y += headerHeight;
        double flowHeight = Math.floor(height - headerHeight);
        layoutInArea(flowAlias, x, y,
                width, flowHeight,
                baselineOffset, HPos.CENTER, VPos.CENTER);
    }


    /**
     * Returns a boolean indicating whether the flow should be layout.
     * This implementation returns true if table is empty.
     * @return
     */
    protected boolean shouldLayoutFlow() {
        return getItemCount() == 0;
    }


    /**
     * {@inheritDoc} </node></t></node></boolean></t></t></p>

* * Overridden to layout the flow always. */ @Override protected void layoutChildren(double x, double y, double width, double height) { super.layoutChildren(x, y, width, height); if (shouldLayoutFlow()) { layoutFlow(x, y, width, height); } } /** * Listener callback from children modifications. * Meant to find the placeholder when it is added. * This implementation passes all added sublists to * hasPlaceHolderRegion for search and install the * placeholder. Removes itself as listener if installed. * * @param c the change */ protected void childrenChanged(Change c) { while (c.next()) { if (c.wasAdded()) { if (installPlaceholderRegion(c.getAddedSubList())) { uninstallChildrenListener(); return; } } } } /** * Installs a ListChangeListener on the children which calls * childrenChanged on receiving change notification. * */ protected void installChildrenListener() { getChildren().addListener(childrenListener); } /** * Uninstalls a ListChangeListener on the children: */ protected void uninstallChildrenListener() { getChildren().removeListener(childrenListener); } }

Пример использования:

public class EmptyPlaceholdersInSkin extends Application {

    private Parent createContent() {
        // initially populated
        //TableView<person> table = new TableView<>(Person.persons()) {
        // initially empty
        TableView<person> table = new TableView<>() {

            @Override
            protected Skin<!--?--> createDefaultSkin() {
                return new NoPlaceHolderTableViewSkin<>(this);
            }

        };
        TableColumn<person, string=""> first = new TableColumn<>("First Name");
        first.setCellValueFactory(new PropertyValueFactory<>("firstName"));

        table.getColumns().addAll(first);

        Button clear = new Button("clear");
        clear.setOnAction(e -> table.getItems().clear());
        clear.disableProperty().bind(Bindings.isEmpty(table.getItems()));
        Button fill = new Button("populate");
        fill.setOnAction(e -> table.getItems().setAll(Person.persons()));
        fill.disableProperty().bind(Bindings.isNotEmpty(table.getItems()));
        BorderPane pane = new BorderPane(table);
        pane.setBottom(new HBox(10, clear, fill));
        return pane;
    }


    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.show();
    }

    public static void main(String[] args) {
        Application.launch(args);
    }

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(EmptyPlaceholdersInSkin.class.getName());

}
</person,></person></person>
 n247s25 янв. 2018 г., 22:01
'исчезновение строк при навигации по стрелке при фальсификации количество предметов должно быть только ошибкой fx9 / fx10, поскольку я не могу воспроизвести его в fx8. В любом случае, хороший обзор этой проблемы / ошибки!

что нашел решение. Это определенно нехорошо, так как он обращается к API нежелательным образом, и яm, вероятно, также делает нежелательное использование visibleProperty, но здесь вы идете:

Вы можете попытаться взломать TableViewSkin. В основном это делается для получения взломанного скина:

public class ModifiedTableView<e> extends TableView<e> {
    @Override
    protected Skin<!--?--> createDefaultSkin() {
        final TableViewSkin<e> skin = new TableViewSkin<e>(this) {
          // override method here
        }
        // modifiy skin here
        return skin;
   }
}
</e></e></e></e>

Для TableViewSkin вам необходимо переопределить следующий метод:

@Override
protected VirtualFlow<tablerow<e>> createVirtualFlow() {
    final VirtualFlow<tablerow<e>> flow = new VirtualFlow<tablerow<e>>();
    // make the 'scroll-region' always visible:
    flow.visibleProperty().addListener((invalidation) -> {
        flow.setVisible(true);
    });
    return flow;
}
</tablerow<e></tablerow<e></tablerow<e>

А для кожи с помощью отражения перестают отображаться заполнители:

final Field privateFieldPlaceholderRegion = TableViewSkinBase.class.getDeclaredField("placeholderRegion");
privateFieldPlaceholderRegion.setAccessible(true);
final StackPane placeholderRegion = (StackPane) privateFieldPlaceholderRegion.get(skin);

// make the 'placeholder' never visible:
placeholderRegion.visibleProperty().addListener((invalidation) -> {
    placeholderRegion.setVisible(false);
});

Может быть, вы можете изменить видимость потока тем же методом, чтобы сделать код короче ... Но я думаю, вы поняли концепцию

но он не использует отражения (к счастью). В основном вам нужно установить (или заменить) скин TableView и вернуть ненулевое значение в методеgetItemCount(), Вот так:

(TableView)t.setSkin(new TableViewSkin<object>(t)
    {
        @Override
        public int getItemCount()
        {
            int r = super.getItemCount();
            return r == 0 ? 1 : r;
        }
    });
<p>Этот метод также можно использовать для добавления дополнительной строки внизу вашего последнего элемента (например, если вы хотите добавить кнопку добавления). По сути, всегда возвращайте на единицу больше, чем фактическое количество предметов.</p><p>Несмотря на то, что это старый вопрос, надеюсь, это кому-то помогло.</p></object>
 kleopatra26 янв. 2018 г., 14:24
проверил, что это хорошо смотрится в fx8 - глюк, который я заметил, это только fx9. +1 за простое решение :)

Вот хитрый способ выполнить вашу задачу,

    HBox box = new HBox();
    box.setDisable(true);
    for (TableColumn column : patientsTable.getColumns()) {
        ListView<string> listView = new ListView<>();
        listView.getItems().add("");
        listView.setPrefWidth(column.getWidth());
        box.getChildren().add(listView);
    }

    tableView.setPlaceholder(box);
</string>
 Xanatos15 янв. 2014 г., 01:08
Ха! Тот'подлый. К сожалению, это нет видправо" в моей таблице, которая имеет много изменяемых размеров столбцов. Однако для простой таблицы со статическими столбцами это может сработать.

что предоставили другие, ниже приведено то, с которым я работал. На мой взгляд, это самый простой подход, который я могу использовать (без пользовательских скинов, без настройки API и нет тяжелых элементов управления, таких как ListView).

Установите StackPane с настроенным CSS, который напоминает альтернативную раскраску строк.

StackPane placeHolder = new StackPane();
placeHolder.setStyle("-fx-background-color:linear-gradient(from 50px 14px to 50px 40px , repeat, #e8e8e8 49% , #f7f7f7 12% );");
tableView.setPlaceholder(placeHolder);

Ниже приведен краткий справочник по реализации. Левая таблица с данными, а правая таблица без данных, показывающая настроенный заполнитель.

Если вы неравнодушны к отображению линий столбцов, вы можете использовать подход @Shreyas Dave для построения HBox из StackPane (s) с реализацией границ.

HBox placeHolder = new HBox();
tableView.getColumns().forEach(tc->{
    StackPane colHolder = new StackPane();
    colHolder.getStyleClass().add("table-view-place-holder");
    colHolder.prefWidthProperty().bind(tc.widthProperty());
    placeHolder.getChildren().add(colHolder);
});
tableView.setPlaceholder(placeHolder);

И реализация CSS выглядит так:

.table-view-place-holder{
  -fx-background-color:linear-gradient(from 50px 14px to 50px 40px , repeat, #232A2D 49% , #3D4549 12% );
  -fx-border-width: 0px 1px 0px 0px;
  -fx-border-color: linear-gradient(from 50px 14px to 50px 40px , repeat, #3D4549 49% , #232a2d 12% );
}

У меня есть требование иметь контрастный цвет границы для фона строки. Я могу легко добиться этого с помощью вышеупомянутого подхода для того, чтобы иметь цвет границы для моих столбцов-заполнителей.

Ваш ответ на вопрос