TableView: настройка количества видимых строк
Я использую эту таблицу для отображения данных в представлении таблицы:
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Pagination;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application
{
IntegerProperty intP = new SimpleIntegerProperty(5);
AnchorPane anchor = new AnchorPane();
Scene scene;
ObservableList<Integer> options
= FXCollections.observableArrayList(
5,
10,
15,
20);
final ComboBox comboBox = new ComboBox(options);
final ObservableList<Person> data = FXCollections.observableArrayList(
new Person("1", "Joe", "Pesci"),
new Person("2", "Audrey", "Hepburn"),
new Person("3", "Gregory", "Peck"),
new Person("4", "Cary", "Grant"),
new Person("5", "De", "Niro"),
new Person("6", "Katharine", "Hepburn"),
new Person("7", "Jack", "Nicholson"),
new Person("8", "Morgan", "Freeman"),
new Person("9", "Elizabeth", "Taylor"),
new Person("10", "Marcello", "Mastroianni"),
new Person("11", "Innokenty", "Smoktunovsky"),
new Person("12", "Sophia", "Loren"),
new Person("13", "Alexander", "Kalyagin"),
new Person("14", "Peter", "OToole"),
new Person("15", "Gene", "Wilder"),
new Person("16", "Evgeny", "Evstegneev"),
new Person("17", "Michael", "Caine"),
new Person("18", "Jean-Paul", "Belmondo"),
new Person("19", " Julia", "Roberts"),
new Person("20", "James", "Stewart"),
new Person("21", "Sandra", "Bullock"),
new Person("22", "Paul", "Newman"),
new Person("23", "Oleg", "Tabakov"),
new Person("24", "Mary", "Steenburgen"),
new Person("25", "Jackie", "Chan"),
new Person("26", "Rodney", "Dangerfield"),
new Person("27", "Betty", "White"),
new Person("28", "Eddie", "Murphy"),
new Person("29", "Amitabh", "Bachchan"),
new Person("30", "Nicole", "Kidman"),
new Person("31", "Adriano", "Celentano"),
new Person("32", "Rhonda", " Fleming's"),
new Person("32", "Humphrey", "Bogart"));
private Pagination pagination;
public static void main(String[] args) throws Exception
{
launch(args);
}
public int itemsPerPage()
{
return 1;
}
public int rowsPerPage()
{
return intP.get();
}
public VBox createPage(int pageIndex)
{
int lastIndex = 0;
int displace = data.size() % rowsPerPage();
if (displace > 0)
{
lastIndex = data.size() / rowsPerPage();
}
else
{
lastIndex = data.size() / rowsPerPage() - 1;
}
VBox box = new VBox();
int page = pageIndex * itemsPerPage();
for (int i = page; i < page + itemsPerPage(); i++)
{
TableView<Person> table = new TableView<>();
TableColumn numCol = new TableColumn("ID");
numCol.setCellValueFactory(new PropertyValueFactory<>("num"));
numCol.setMinWidth(20);
TableColumn firstNameCol = new TableColumn("First Name");
firstNameCol.setCellValueFactory(new PropertyValueFactory<>("firstName"));
firstNameCol.setMinWidth(160);
TableColumn lastNameCol = new TableColumn("Last Name");
lastNameCol.setCellValueFactory(new PropertyValueFactory<>("lastName"));
lastNameCol.setMinWidth(160);
table.getColumns().addAll(numCol, firstNameCol, lastNameCol);
if (lastIndex == pageIndex)
{
table.setItems(FXCollections.observableArrayList(data.subList(pageIndex * rowsPerPage(), pageIndex * rowsPerPage() + displace)));
}
else
{
table.setItems(FXCollections.observableArrayList(data.subList(pageIndex * rowsPerPage(), pageIndex * rowsPerPage() + rowsPerPage())));
}
box.getChildren().addAll(table);
}
return box;
}
@Override
public void start(final Stage stage) throws Exception
{
scene = new Scene(anchor, 450, 450);
comboBox.valueProperty().addListener(new ChangeListener<Number>()
{
@Override
public void changed(ObservableValue o, Number oldVal, Number newVal)
{
//System.out.println(newVal.intValue());
intP.set(newVal.intValue());
paginate();
}
});
paginate();
stage.setScene(scene);
stage.setTitle("Table pager");
stage.show();
}
public void paginate()
{
pagination = new Pagination((data.size() / rowsPerPage() + 1), 0);
// pagination = new Pagination(20 , 0);
// pagination.setStyle("-fx-border-color:red;");
pagination.setPageFactory(new Callback<Integer, Node>()
{
@Override
public Node call(Integer pageIndex)
{
if (pageIndex > data.size() / rowsPerPage() + 1)
{
return null;
}
else
{
return createPage(pageIndex);
}
}
});
AnchorPane.setTopAnchor(pagination, 10.0);
AnchorPane.setRightAnchor(pagination, 10.0);
AnchorPane.setBottomAnchor(pagination, 10.0);
AnchorPane.setLeftAnchor(pagination, 10.0);
AnchorPane.setBottomAnchor(comboBox, 40.0);
AnchorPane.setLeftAnchor(comboBox, 12.0);
anchor.getChildren().clear();
anchor.getChildren().addAll(pagination, comboBox);
}
public static class Person
{
private final SimpleStringProperty num;
private final SimpleStringProperty firstName;
private final SimpleStringProperty lastName;
private Person(String id, String fName, String lName)
{
this.firstName = new SimpleStringProperty(fName);
this.lastName = new SimpleStringProperty(lName);
this.num = new SimpleStringProperty(id);
}
public String getFirstName()
{
return firstName.get();
}
public void setFirstName(String fName)
{
firstName.set(fName);
}
public String getLastName()
{
return lastName.get();
}
public void setLastName(String fName)
{
lastName.set(fName);
}
public String getNum()
{
return num.get();
}
public void setNum(String id)
{
num.set(id);
}
}
}
Когда я изменяю количество строк с помощью поля "Комбо", меняются только данные в строках таблицы. Высота стола не изменяется.
![enter image description here]()
Есть ли способ удалить пустые строки?
Ответы
Ответ 1
Изменение высоты таблицы и удаление "пустых" строк - это две разные вещи. Быть конкретной.
Для удаления строк см. этот учебник.
Для изменения высоты сначала установите fixedCellSizeProperty
в виде таблицы, затем используйте его в привязке:
table.setFixedCellSize(25);
table.prefHeightProperty().bind(Bindings.size(table.getItems()).multiply(table.getFixedCellSize()).add(30));
Добавление 30px для заголовка таблицы.
Ответ 2
К сожалению, конфигурация visibleRowCount не поддерживается в TableView (вы можете рассмотреть возможность подачи запроса функции в fx 'jira - не нужно, уже сделано много лет назад). И это не совсем просто, чтобы представление возвращало prefHeight на основе такого предпочтения: нам нужно будет измерять требования к размеру "реальной" ячейки и как-то зарываться внутри кишечника.
Просто для удовольствия, экспериментировал с расширением всего пакета сотрудников:
- пользовательский tableView, обладающий свойством visibleRowCount
- пользовательский скин, который прослушивает свойство, вычисляет его prefHight в зависимости от него.
- каким-либо образом получить доступ к высоте "реальной" ячейки - единственный класс со всей информацией для его измерения - это VirtualFlow. Поскольку соответствующий метод защищен, для этого требуется либо настраиваемый VirtualFlow, который предоставляет этот метод, либо отражающий доступ.
Код:
/**
* TableView with visibleRowCountProperty.
*
* @author Jeanette Winzenburg, Berlin
*/
public class TableViewWithVisibleRowCount<T> extends TableView<T> {
private IntegerProperty visibleRowCount = new SimpleIntegerProperty(this, "visibleRowCount", 10);
public IntegerProperty visibleRowCountProperty() {
return visibleRowCount;
}
@Override
protected Skin<?> createDefaultSkin() {
return new TableViewSkinX<T>(this);
}
/**
* Skin that respects table visibleRowCount property.
*/
public static class TableViewSkinX<T> extends TableViewSkin<T> {
public TableViewSkinX(TableViewWithVisibleRowCount<T> tableView) {
super(tableView);
registerChangeListener(tableView.visibleRowCountProperty(), "VISIBLE_ROW_COUNT");
handleControlPropertyChanged("VISIBLE_ROW_COUNT");
}
@Override
protected void handleControlPropertyChanged(String p) {
super.handleControlPropertyChanged(p);
if ("VISIBLE_ROW_COUNT".equals(p)) {
needCellsReconfigured = true;
getSkinnable().requestFocus();
}
}
/**
* Returns the visibleRowCount value of the table.
*/
private int getVisibleRowCount() {
return ((TableViewWithVisibleRowCount<T>) getSkinnable()).visibleRowCountProperty().get();
}
/**
* Calculates and returns the pref height of the
* for the given number of rows.
*
* If flow is of type MyFlow, queries the flow directly
* otherwise invokes the method.
*/
protected double getFlowPrefHeight(int rows) {
double height = 0;
if (flow instanceof MyFlow) {
height = ((MyFlow) flow).getPrefLength(rows);
}
else {
for (int i = 0; i < rows && i < getItemCount(); i++) {
height += invokeFlowCellLength(i);
}
}
return height + snappedTopInset() + snappedBottomInset();
}
/**
* Overridden to compute the sum of the flow height and header prefHeight.
*/
@Override
protected double computePrefHeight(double width, double topInset,
double rightInset, double bottomInset, double leftInset) {
// super hard-codes to 400 .. doooh
double prefHeight = getFlowPrefHeight(getVisibleRowCount());
return prefHeight + getTableHeaderRow().prefHeight(width);
}
/**
* Reflectively invokes protected getCellLength(i) of flow.
* @param index the index of the cell.
* @return the cell height of the cell at index.
*/
protected double invokeFlowCellLength(int index) {
double height = 1.0;
Class<?> clazz = VirtualFlow.class;
try {
Method method = clazz.getDeclaredMethod("getCellLength", Integer.TYPE);
method.setAccessible(true);
return ((double) method.invoke(flow, index));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
return height;
}
/**
* Overridden to return custom flow.
*/
@Override
protected VirtualFlow createVirtualFlow() {
return new MyFlow();
}
/**
* Extended to expose length calculation per a given # of rows.
*/
public static class MyFlow extends VirtualFlow {
protected double getPrefLength(int rowsPerPage) {
double sum = 0.0;
int rows = rowsPerPage; //Math.min(rowsPerPage, getCellCount());
for (int i = 0; i < rows; i++) {
sum += getCellLength(i);
}
return sum;
}
}
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger.getLogger(TableViewWithVisibleRowCount.class
.getName());
}
Обратите внимание, что вы можете уйти с простым переопределением таблицы prefHeight при наличии фиксированной ячейки, не пытайтесь это сделать - без риска не весело: -)
Ответ 3
Здесь мое решение, чтобы не зависеть от table.getFixedCellSize
(мы все еще зависим от него во время инициализации FX, а CSS еще не вычисляется/не применяется).
Обратите внимание, что нам также нужно добавить несколько пикселей (не понимаю почему).
public static <S> void ensureDisplayingRows(@NotNull TableView<S> table, @Null Integer rowCount) {
DoubleProperty headerRowHeightProperty = new SimpleDoubleProperty();
table.skinProperty().addListener((observable, oldValue, newValue) -> {
if (!Objects.equals(oldValue, newValue)) {
TableHeaderRow headerRow = headerRow(table);
// TableHeaderRow not defined until CSS is applied.
if (headerRow == null) {
assert table.getFixedCellSize() > 0.0 : "TableView '" + table.getId() + "' is not 'fixedCellSize'."; // TODO Find a better way to control.
headerRowHeightProperty.setValue(table.getFixedCellSize()); // Approximation. // TODO Find a better approximation.
} else {
headerRowHeightProperty.bind(headerRow.heightProperty());
}
}
});
IntegerBinding itemsCountBinding = Bindings.size(table.getItems()); // NB: table.getItems() may not (yet) contains all/"new" items, may contain the "old" items.
IntegerBinding maxRowsCountBinding = (rowCount == null) ? itemsCountBinding :
(IntegerBinding) Bindings.min(
rowCount,
itemsCountBinding
);
IntegerBinding rowCountBinding = (IntegerBinding) Bindings.max(
1, // Ensure to display at least 1 row, for JavaFX "No contents" message when table.items.isEmpty.
maxRowsCountBinding
);
DoubleBinding tableHeightBinding = headerRowHeightProperty
.add(rowCountBinding.multiply(table.getFixedCellSize()))
.add(10); // TODO Understand why we need to add a dozen of pixels.
table.minHeightProperty().bind(tableHeightBinding);
table.prefHeightProperty().bind(tableHeightBinding);
table.maxHeightProperty().bind(tableHeightBinding);
}
@Null
public static TableHeaderRow headerRow(@NotNull TableView<?> table) {
TableHeaderRow tableHeaderRow = (TableHeaderRow) table.lookup("TableHeaderRow");
return tableHeaderRow;
}
Ответ 4
Есть ли способ сделать это... да, вам нужно сделать, когда вы создаете таблицу (так как вы заново создаете ее каждый раз, когда вы выбираете новый номер) вам нужно вычислить, какую высоту таблица с текущим количеством записей, а затем используйте свойство setPrefHeight()
TableView, чтобы уменьшить таблицу для учета только этих строк.
Я немного поиграл с этим, и я не нашел быстрых решений для расчета размера таблицы, поэтому у меня нет кода для вас, но это то, что вам нужно сделать, Вы также можете "стилизовать" таблицу, чтобы не иметь чередующейся цветовой схемы, которая сделала бы строки ниже тех, у которых данные выглядели "пустыми", хотя было бы пустое пространство.
Удачи!
Ответ 5
Просто измените цвет фона пустых строк, используя css
.table-row-cell:empty {
-fx-background-color: white;
-fx-border-color: white;
}
и изменить количество строк на основе combobox.
Ответ 6
Если вы не привязаны к привязкам, простой способ сделать это - вычислить желаемую высоту в зависимости от размера фиксированной ячейки (см. Ответ Фреда Данны) и обновить его слушателем в данных таблицы.
static void setTableHeightByRowCount(TableView table, ObservableList data) {
int rowCount = data.size();
TableHeaderRow headerRow = (TableHeaderRow) table.lookup("TableHeaderRow");
double tableHeight = (rowCount * table.getFixedCellSize())
// add the insets or we'll be short by a few pixels
+ table.getInsets().getTop() + table.getInsets().getBottom()
// header row has its own (different) height
+ (headerRow == null ? 0 : headerRow.getHeight())
;
table.setMinHeight(tableHeight);
table.setMaxHeight(tableHeight);
table.setPrefHeight(tableHeight);
}
В start(Stage)
мы создаем таблицу и добавляем ListChangeListener
:
TableView<String> table = new TableView<>();
table.setFixedCellSize(24);
table.getItems().addListener((ListChangeListener<String>) c ->
setTableHeightByRowCount(table, c.getList()));
// init scene etc...
stage.show();
table.getItems().addAll("Stacey", "Kristy", "Mary Anne", "Claudia");
Обратите внимание, что строка заголовка таблицы не существует до тех пор, пока не будет stage.show()
, поэтому проще всего подождать, чтобы установить данные таблицы до этого.
В качестве альтернативы мы могли бы установить данные на время построения таблицы, а затем явно называть setTableHeightByRowCount()
:
TableView<String> table = new TableView<>(
FXCollections.observableArrayList("Stacey", "Kristy", "Mary Anne", "Claudia")
);
// add listener, init scene etc...
stage.show();
setTableHeightByRowCount(table, table.getItems());