-
Notifications
You must be signed in to change notification settings - Fork 5
Span cells
This tutorial explains how to merge cells in a table itself (not in a header).
We'll take quick start source as the starting point and change its data:
data.setValue( 0, "FIRST_NAME", "John" );
data.setValue( 0, "LAST_NAME", "Doe" );
data.setValue( 1, "FIRST_NAME", "Jane" );
data.setValue( 1, "LAST_NAME", "Doe" );
data.setValue( 2, "FIRST_NAME", "Anony" );
data.setValue( 2, "LAST_NAME", "Mouse" );
data.setValue( 3, "FIRST_NAME", "William" );
data.setValue( 3, "LAST_NAME", "Perry" );
data.setValue( 4, "FIRST_NAME", "Morgan" );
data.setValue( 4, "LAST_NAME", "McQueen" );
data.setValue( 5, "FIRST_NAME", "Katie" );
data.setValue( 5, "LAST_NAME", "McQueen" );
data.setValue( 6, "FIRST_NAME", "Albert" );
data.setValue( 6, "LAST_NAME", "Newmann" );
data.setValue( 7, "FIRST_NAME", "John" );
data.setValue( 7, "LAST_NAME", "Goode" );
data.setValue( 8, "FIRST_NAME", "Vanessa" );
data.setValue( 8, "LAST_NAME", "Key" );
data.setValue( 9, "FIRST_NAME", "Robert" );
data.setValue( 9, "LAST_NAME", "Peterson" );
We would also need a row sorter (it's a standard Swing row sorter):
table.setAutoCreateRowSorter( true );
A picture we get:
JRubik library (it's listed in the Alternatives section) lets to manually merge cells at the view level. Another approach was chosen for JBroTable: cells merging rules are set at the model level. And view determines at rendering stage if cells need to be merged according to these rules. These rules may seem weird and hard to customize at first glance but it's much easier to solve tasks like row sorting or columns rearrangement using this approach.
Suppose we want to merge cells in the LAST_NAME
column for equal last names.
table.setUI( new JBroTableUI()
.withSpan( new ModelSpan( "LAST_NAME", "LAST_NAME" ).withColumns( "LAST_NAME" ) ) );
Using this code we get the following picture:
Cells with values McQueen
and Doe
are merged. Try to sort by FIRST_NAME
column. The sorting breaks the order of McQueen
family. Robert Peterson
stands now between Morgan
and Katie
. Oh, poor Morgan
! And cells with value McQueen
aren't merged anymore. The cell Doe
is still merged because the order here is sequential:
This example uses only LAST_NAME
column written 3
times, so it's unclear what are these three arguments needed for.
Why did I decide that Morgan
and Katie McQueen
are married? Equivalence of last names doesn't necessarily mean that. The McQueen
last name is quite frequent. We need to distinguish the families by some identifier. It would be more reliable.
IModelFieldGroup groups[] = new IModelFieldGroup[] {
new ModelField( "USER_ID", "User identifier" ),
new ModelField( "FAMILY_ID", "Family identifier" )
.withVisible( false ),
new ModelFieldGroup( "NAME", "Person name" )
.withChild( new ModelField( "FIRST_NAME", "First name" ) )
.withChild( new ModelField( "LAST_NAME", "Last name" ) ),
new ModelField( "PHONE", "Phone number" )
};
We have added a new field FAMILY_ID
and have hidden it (usually, end-users don't need to see the database internal identifiers). Let's fill the values:
for ( int i = 0; i < rows.length; i++ )
data.setValue( i, "FAMILY_ID", i );
Every person has a separate family now. Let's define the real families. Let Doe
be one family and McQueen
be just a coincidence.
data.setValue( 1, "FAMILY_ID", 0 );
Redefine our span model:
table.setUI( new JBroTableUI()
.withSpan( new ModelSpan( "FAMILY_ID", "LAST_NAME" ).withColumns( "LAST_NAME" ) ) );
Now cells would be merged only when the FAMILY_ID
value of their row matches.
So, the first argument of the ModelSpan
constructor is the identifier field. If the value of identifier field of current row equals to the value of identifier field of the next row then the cells of these rows would be merged.
What is the second argument needed for? Try to replace it with FIRST_NAME
:
table.setUI( new JBroTableUI()
.withSpan( new ModelSpan( "FAMILY_ID", "FIRST_NAME" ).withColumns( "LAST_NAME" ) ) );
It defines what value should be shown in the merged cells:
Usually, second argument (merged cells caption) should have value that depends on the identifier: the identifier itself or a literal description of the identifier. For instance, LAST_NAME
strongly depends on FAMILY_ID
. Two rows with the same FAMILY_ID
would definitely have the same LAST_NAME
. The opposite is not always true.
This restriction isn't obligatory. You can see it in our example: FIRST_NAME
doesn't depend on FAMILY_ID
. Two rows with the same FAMILY_ID
may have different FIRST_NAME
s (Jane
and John Doe
). If this restriction is violated then the value of merged cell may differ depending on rows order. Try to sort our last example by FIRST_NAME
. The value of merged cell John
changes to Jane
on sorting. Use this restriction to avoid ambiguity.
At last, what do the arguments in method withColumns
mean? Let's try this snippet:
table.setUI( new JBroTableUI()
.withSpan( new ModelSpan( "FAMILY_ID", "LAST_NAME" ).withColumns( "LAST_NAME", "PHONE" ) ) );
And we get the following picture:
This variable length argument (vararg) defines which columns should be merged according to this rule. So, we have merged cells of LAST_NAME
and PHONE
columns for the rows having the same FAMILY_ID
. And the value of merged cells equals to the value of LAST_NAME
column.
Try to drag the LAST_NAME
or PHONE
column and move it to the left. A merged cell is now divided by FIRST_NAME
column. And it isn't merged anymore (except the Doe
rows).
Take the full source code of this tutorial here.
Consider viewing an advanced span cells tutorial also.