javafx - llenar - TableView: ajusta el número de filas visibles
tableview javafx css (6)
¿Hay alguna manera de hacerlo ... sí, lo que debe hacer es crear la tabla (ya que la vuelve a crear cada vez que selecciona un nuevo número) necesita calcular con qué altura de la tabla está el número actual de entradas, y luego use la propiedad
setPrefHeight()
de TableView para hacer que la tabla sea más pequeña para tener en cuenta solo esas filas.
Jugué un poco, y no encontré ninguna solución rápida para calcular el tamaño de la tabla correctamente, así que no tengo ningún código para ti, pero eso es lo que debes hacer. También podría ''diseñar'' la tabla para que no tenga el esquema de color alternativo, lo que haría que las filas debajo de las que tienen datos se vean ''vacías'' a pesar de que hubiera algún espacio en blanco.
¡Buena suerte!
Estoy usando esta tabla para mostrar datos en la Vista de tabla:
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);
}
}
}
Cuando cambio el número de filas usando el cuadro combinado, solo cambian los datos en las filas de la tabla. La altura de la mesa no cambia.
¿Hay alguna forma de eliminar filas vacías?
Aquí está mi solución, para no depender profundamente de
table.getFixedCellSize
(todavía dependemos de ello durante la inicialización de FX, mientras que CSS aún no se calcula / aplica).
Tenga en cuenta que también necesitamos agregar algunos píxeles (no entiendo por qué).
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;
}
Cambiar la altura de la vista de tabla y eliminar filas "vacías" son dos cosas diferentes. Se específico.
Para eliminar filas, vea fxexperience.com/2011/11/… .
Para cambiar la altura, primero establezca
fixedCellSizeProperty
de la vista de tabla y luego
fixedCellSizeProperty
en el enlace:
table.setFixedCellSize(25);
table.prefHeightProperty().bind(Bindings.size(table.getItems()).multiply(table.getFixedCellSize()).add(30));
Agregar 30 px es para el encabezado de tableview.
Desafortunadamente, la configuración de visibleRowCount no es compatible con TableView (puede considerar presentar una solicitud de función en fx ''jira; no es necesario, ya se hizo hace años ). Y no es del todo sencillo dejar que la vista devuelva una altura de preferencia basada en una preferencia de este tipo: tendremos que medir los requisitos de tamaño de la celda "real" y eso de alguna manera está enterrado dentro de los intestinos.
Solo por diversión, experimente con la extensión de toda la pila de colaboradores:
- una tableView personalizada que tiene una propiedad visibleRowCount
- una máscara personalizada que escucha la propiedad calcula su altura de preferencia dependiendo de ella
- alguna forma de acceder a la altura de la celda "real": la única clase con toda la información para medirla es VirtualFlow. Como el método relevante está protegido, esto requiere un VirtualFlow personalizado que expone ese método o acceso reflexivo.
El código:
/**
* 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''s 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());
}
Tenga en cuenta que puede salirse con la suya con una simple anulación de prefHeight de la tabla cuando tenga un tamaño de celda fijo, no lo intentó, sin riesgo no es divertido :-)
Si no está casado con los enlaces, una forma sencilla de hacerlo es calcular la altura deseada en función del tamaño de celda fijo (véase la respuesta de Fred Danna ) y actualizarlo con un oyente en los datos de la tabla.
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);
}
En
start(Stage)
, creamos la tabla y agregamos un
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");
Tenga en cuenta que la fila del encabezado de la tabla no existe hasta después de
stage.show()
, por lo que lo más sencillo es esperar para establecer los datos de la tabla hasta entonces.
Alternativamente, podríamos establecer los datos en el momento de la construcción de la tabla, y luego llamar a
setTableHeightByRowCount()
explícitamente:
TableView<String> table = new TableView<>(
FXCollections.observableArrayList("Stacey", "Kristy", "Mary Anne", "Claudia")
);
// add listener, init scene etc...
stage.show();
setTableHeightByRowCount(table, table.getItems());
Simplemente cambie el color de fondo de las filas vacías usando CSS
.table-row-cell:empty {
-fx-background-color: white;
-fx-border-color: white;
}
y modifique el número de filas en función del cuadro combinado.