Post on 25-Feb-2016
description
TS-4706 Bringing JTable to the ExtremeDavid QiaoJIDE 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
2
Goal> Explore the internals of JTable > Find a way to enhance it to meet requirements
3
JTable: 101
4
JTableTableModel
TableUI (BasicTableUI)
JTableHeaderTableColumn
TableColumnModel
TableCellRendererTableCellEditor
JTable> Nothing but a bunch of cells arranged in grid-like
layout> Not enough in an real world application
5
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)
6
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)
7
Possible Enhancements: Table Model> Sorting> Filtering> Grouping> Caching> Paginating> JavaBean Support> JDBC or Hibernate Support
8
Possible Enhancements: Renderer/Editor> More cell renderers and editors for various data types
Numbers Color Date/Calendar Font Insets Array
9
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
10
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
13
Case Study: Filtering and Sorting> The original TableModel should NOT be touched> Performance and memory> Scalability
14
Demo of Sortable and Filterable Feature
15
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
16
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
TableModel getActualModel()> RowTableModelWrapper
Provides row index mapping int getActualRowAt(int visualRowIndex) int getVisualRowAt(int actualRowIndex)
SortableTableModel
3 06 10 21 35 42 58 69 77 84 9
SortableTableModel(set to a table)
Actual TableModel
.
.
.
Implementation of getValueAt methodpublic 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].
19
FilterableTableModel
0 03 19 2
3456789
FilterableTableModel(set to a table)
Actual TableModel
TableModelWrapper “Pipes”Sortable
TableModel (1)Actual
TableModel (1)
1 0 0
2 3 1
0 9 2
3
4
5
6
7
8
9
FilterableTableModel (n)
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 array is not created until getVisualRowAt is
called22
SortableTable> SortableTableHeader to allow click-to-sort.
23
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
24
If there were one thing to learn …> Remember the row index mapping idea and the table
model wrapper design.
25
Case Style: Cell Spanning
26
> Merge several cells into one big cell
Demo of CellSpanTable
27
Brainstorming> Model: store the cell spans> View: change how the grid lines are painted
28
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
CellSpan(1,2,3,4)
row, column, rowSpan, columnSpan
SpanModel> Methods:
boolean isCellSpanOn() CellSpan getCellSpanAt(int row, int column)
> Any TableModel can implement this SpanModel interface
> Implementations: AbstractSpanTableModel DefaultSpanTableModel: addCellSpan, removeCellSpan
etc. methods
Exampleclass MyModel extends AbstractTableModel implements 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; } }
31
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); }
34
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)
35
If there were one thing to learn …> Store the information along the table model by
implementing a new interface
36
Case Study: Cell Styling> Background (solid color or gradient)> Foreground> Font (font name, style, size)> Border> Alignment> Icon> Row stripes/column stripes
37
Demo of CellStyleTable
38
Brainstorming> Is cell styling a model concept or a view concept?
39
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
40
Brainstorming (Cont.)> Providing the Cellstyle
Using TableModel – for model related styles Using JTable – for non-model related styles
41
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() CellStyle getCellStyleAt(int row, int column)
Subclass JTable - CellStyleTable> Add setTableStyleProvider
public interface TableStyleProvider { CellStyle getCellStyleAt(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?
45
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.
46
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
47
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.
48
Showcases
49
Q & A
50
51
David QiaoJIDE Software, Inc.david@jidesoft.com