510 likes | 812 Vues
TS-4706 Bringing JTable to the Extreme. David Qiao JIDE Software, Inc. About JIDE. JIDE Software is a Swing component provider Over 12 different products Also provide L&F, UI and icon design service JIDE Docking Framework JIDE Grids JIDE Pivot Grid JIDE Data Grids
E N D
TS-4706 Bringing JTable to the Extreme David Qiao JIDE Software, Inc.
About JIDE • JIDE Software is a Swing component provider • Over 12 different products • Also provide L&F, UI and icon design service • JIDE Docking Framework • JIDE Grids • JIDE Pivot Grid • JIDE Data Grids • JIDE Desktop Application Framework
Goal • Explore the internals of JTable • Find a way to enhance it to meet requirements
JTable: 101 JTable TableModel TableUI (BasicTableUI) TableCellRenderer TableCellEditor JTableHeader TableColumn TableColumnModel
JTable • Nothing but a bunch of cells arranged in grid-like layout • Not enough in an real world application
Possible Enhancements: Table Header • AutoFilter (Excel style filter dropdowns) • Multiple Rows in the Header with Column Span (nested table header) • Sortable/Groupable • Show/Hide Columns (right click context menu) • Column Auto-Fit (double click on the column divider)
Possible Enhancements: Table Body • Cell Spanning (merge cells) • Frozen Rows/Columns (make some rows or columns not scrollable) • Resizable Rows/Columns (drag the grid lines to resize) • Cell Styling (row/column stripes, back/foreground color, font, flashing) • TreeTable • HierarchicalTable (put other components inside table under each row)
Possible Enhancements: Table Model • Sorting • Filtering • Grouping • Caching • Paginating • JavaBean Support • JDBC or Hibernate Support
Possible Enhancements: Renderer/Editor • More cell renderers and editors for various data types • Numbers • Color • Date/Calendar • Font • Insets • Array
Possible Enhancements: Other • State persistence (column order, width, sort order, selection) • Key navigation (i.e. only stop on editable cells) • Excel export • Non-contiguous cell selection • Formula (like Excel) • Copy and paste (copy is supported by JTable but paste is missing) • Cell validation and row validation
Things to Consider • Compatible with JTable • JTable is still moving along, there is no reason to start from scratch. • Won’t break the code for every new JDK release. • Compatible with existing code which already uses JTable • Make migration easier • Developer feels at home • Cons: face some limitations
Things we can leverage • Subclassing • JTable • BasicTableUI • TableModel • Overriding • JTable: prepareRenderer, prepareEditor, changeSelection, isCellSelected, rowAtPoint etc. • BasicTableUI: paint • Leveraging existing JTable APIs • listeners, keyboard actions
Case Studies • Filtering and Sorting • Cell Spanning • Cell Styling
Case Study: Filtering and Sorting • The original TableModel should NOT be touched • Performance and memory • Scalability
The solution is … • View/Model Conversion • Using a row index mapping to map from the row index from the actual model to the row index in the view • Nothing new • TableColumnModel is such a view/model conversion except it is for the columns
TableModelWrapper (or delegate) • It wraps any TableModel to provide a mapping of row indices from the outer model to the inner model • Only one method • TableModelgetActualModel() • RowTableModelWrapper • Provides row index mapping • intgetActualRowAt(intvisualRowIndex) • intgetVisualRowAt(intactualRowIndex)
SortableTableModel Actual TableModel SortableTableModel (set to a table) . . .
Implementation of getValueAt method public Object getValueAt(int row, int column) { if (_indexes != null && (row < 0 || row >= _indexes.length)) { return null; } else { return _model.getValueAt(getActualRowAt(row), column); } } where getActualRowAt is _indexes[row].
FilterableTableModel Actual TableModel FilterableTableModel (set to a table)
TableModelWrapper “Pipes” Sortable TableModel (1) Filterable TableModel (n) Actual TableModel (1) JTable
Performance and Memory • The lookup speed: a direct array access for each table model wrapper • table.getValueAt(row, column) calls tableModel.getValueAt(getActualRowAt(row), column) • Memory consumption: one (or two) int array whose length is the same as the row count • Optional index caching feature to make reverse lookup to make getVisualRowAt method faster • Lazy initialization • The index array is not created until there is sorting • The reverse index arrayis not created until getVisualRowAt is called
SortableTable • SortableTableHeader to allow click-to-sort.
Filterable related components • Different from SortableTable, no FilterableTable • Any JTable can be filterable if you set a FilterableTableModel on to it • Other components work with JTable and FilterableTableModel to make it easy for user to add/remove filters • AutoFilterTableHeader • QuickTableFilterField (optionally use Lucene) • QuickFilterPane • TableCustomFilterEditor
If there were one thing to learn … • Remember the row index mapping idea and the table model wrapper design.
Case Style: Cell Spanning • Merge several cells into one big cell
Brainstorming • Model: store the cell spans • View: change how the grid lines are painted
CellSpan • _row: Span start row index • _column: Span start column index • _rowSpan: The number of rows that it spans • _columnSpan: The number of columns that it spans
SpanModel • Methods: • boolean isCellSpanOn() • CellSpan getCellSpanAt(int row, int column) • Any TableModel can implement this SpanModel interface • Implementations: • AbstractSpanTableModel • DefaultSpanTableModel: addCellSpan, removeCellSpan etc. methods
Example class MyModel extends AbstractTableModelimplements SpanModel { ……. // all other table model methods CellSpan span; public CellSpan getCellSpanAt(int row, int column) { // define the span based on the row and column index return span; } public boolean isCellSpanOn() { return true; } }
Subclassing BasicTableUI • BasicCellSpanTableUI extends BasicTableUI • The paintGrid and paintCells methods are private • End up overriding paint(g, c) method with many duplicated code
Subclassing JTable • CellSpanTable • Override getCellRenderer, prepareRenderer, getCellEditor, prepareEditor, editCellAt • Override rowAtPoint, columnAtPoint and getCellRect • Override isCellSelected
getCellRenderer @Override public TableCellRenderer getCellRenderer(int row, int column) { CellSpan cellSpan = getCellSpanAt(row, column); if (cellSpan != null) { return super.getCellRenderer(cellSpan.getRow(), cellSpan.getColumn()); } return super.getCellRenderer(row, column); }
Other Considerations • Caching CellSpans at CellSpanTable • Caching the painting of CellSpans in BasicCellSpanTableUI to avoid the cells in the same CellSpan painted again and again • Converting the CellSpan when columns are rearranged (not all possible)
If there were one thing to learn … • Store the information along the table model by implementing a new interface
Case Study: Cell Styling • Background (solid color or gradient) • Foreground • Font (font name, style, size) • Border • Alignment • Icon • Row stripes/column stripes
Brainstorming • Is cell styling a model concept or a view concept?
Brainstorming • Is cell styling a model concept or a view concept? • It depends • Row striping is certain a view concept because it has nothing to do with the table data • However “displaying red text for negative values” is related to the table data so it is kind of a model concept
Brainstorming (Cont.) • Providing the Cellstyle • Using TableModel – for model related styles • Using JTable – for non-model related styles
CellStyle • A class define styles mentioned on the previous slide that has many setters and getters, such as • Color getForeground() • void setForeground(Color color) • Border getBorder() • void setBorder(Border border)
StyleModel • Any TableModel can implement the StyleModel interface • Methods: • boolean isCellStyleOn() • CellStylegetCellStyleAt(int row, int column)
Subclass JTable - CellStyleTable • Add setTableStyleProvider public interface TableStyleProvider { CellStylegetCellStyleAt(JTable table, int rowIndex, int columnIndex); } • Override prepareRenderer/prepareEditor methods
Implements Foreground Style • prepareRenderer(renderer, row, column); • Call super to get the rendererComponent • Gets the CellStyle(s) from the StyleModel/TableStyleProvider • If CellStyle has a foreground defined, call rendererComponent.setForeground • Repeat the previous step to cover all styles • BasicTableUI will then paint the rendererComponent on the table cell • What will happen if we only do this?
Implements Foreground Style • prepareRenderer(renderer, row, column); • Call super to get the rendererComponent • Gets the CellStyle(s) from the StyleModel/TableStyleProvider • If CellStyle has a foreground defined, call rendererComponent.setForeground • Repeat the previous step to cover all styles • BasicTableUI will then paint the rendererComponent on the table cell • What will happen if we only do this? • Because the same renderer is used in a table for different cells, all those cells will have the same foreground.
Implements Foreground Style: Cont. • releaseRendererComponent(renderer, row, column, rendererComponent) • We changed TableUI to always call releaseRendererComponent after the rendererComponent is painted. • And we reset the foreground to its previous value in this method • We suggest Sun includes this method in JTable
If there were one thing to learn … • Define cell styling in a consistent way for all the tables in your application • Define all CellStyle instances in one place • You can even create CellStyle on fly using stylesheet or a configuration file when application starts. • getCellStyleAt return the predefined instance.