TableView: adjust number of visible rows

前端 未结 6 544
梦谈多话
梦谈多话 2020-11-29 06:30

I\'m using this table to display data in Table View:

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans         


        
6条回答
  •  攒了一身酷
    2020-11-29 07:22

    Unfortunately, configuration of the visibleRowCount isn't supported in TableView (you might consider filing a feature request in fx' jira - no need, already done years ago). And it's not entirely straightforward to let the view return a prefHeight based on a such a preference: we'll need to measure the size requirements of the "real" cell and that's somehow buried inside the bowels.

    Just for fun, experimented with extending the whole stack of collaborators:

    • a custom tableView that has a visibleRowCount property
    • a custom skin that listens to the property calculates its prefHeight depending on it
    • some way to access the height of the "real" cell - the only class with all info to measure it, is the VirtualFlow. As the relevant method is protected, this requires either a custom VirtualFlow that exposes that method or reflective access.

    The code:

    /**
     * TableView with visibleRowCountProperty.
     * 
     * @author Jeanette Winzenburg, Berlin
     */
    public class TableViewWithVisibleRowCount extends TableView {
    
        private IntegerProperty visibleRowCount = new SimpleIntegerProperty(this, "visibleRowCount", 10);
        
        
        public IntegerProperty visibleRowCountProperty() {
            return visibleRowCount;
        }
        
        @Override
        protected Skin createDefaultSkin() {
            return new TableViewSkinX(this);
        }
        
        /**
         * Skin that respects table's visibleRowCount property.
         */
        public static class TableViewSkinX extends TableViewSkin {
    
            public TableViewSkinX(TableViewWithVisibleRowCount 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) 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());
    }
    

    Note that you might get away with a plain override of table's prefHeight when having a fixed-cell-size, didn't try that - no risk no fun :-)


    Update: the custom skin in javafx15 - basically the same, just some access details changed (in both directions ;)

    /**
     * Skin that respects table's visibleRowCount property.
     */
    public class TableViewSkinX extends TableViewSkin {
    
        public TableViewSkinX(TableViewWithVisibleRowCount tableView) {
            super(tableView);
            registerChangeListener(tableView.visibleRowCountProperty(), e -> visibleRowCountChanged());
        }
        
        private void visibleRowCountChanged() {
            getSkinnable().requestLayout();
        }
        
        /**
         * Returns the visibleRowCount value of the table.
         */
        private int getVisibleRowCount() {
            return ((TableViewWithVisibleRowCount) getSkinnable()).visibleRowCountProperty().get();
        }
        
        /**
         * Calculates and returns the pref height of the for the given number of
         * rows.
         */
        protected double getFlowPrefHeight(int rows) {
            double height = 0;
            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) {
            // note: use your own utility method to reflectively access internal fields/methods
            return (double) FXUtils.invokeGetMethodValue(VirtualFlow.class, getVirtualFlow(), 
                    "getCellLength", Integer.TYPE, index);
        }
    
    }
    

提交回复
热议问题