Java Tips Weblog

  • Blog Stats

    • 2,569,663 hits
  • Categories

  • Archives

Fixed Column Table

Posted by Rob Camick on November 5, 2008

A JTable is usually displayed in a JScrollPane. This allows the table to scroll vertically or horizontally as required. Horizontal scrolling causes all columns in the table to be scrolled. In some cases you may want to prevent the leading column(s) in the table from scrolling.

JTable does not support this functionality but in combination with a JScrollPane we can achieve the desired functionality. The FixedColumnTable class is used as a convenience class to manipulate the table and scroll pane. Behind the scenes your main table is split into two. The second table will display the fixed columns and is added to the scroll panes row header. This fixed table will share the TableModel and SelectionModel with the main table.

You will create the FixedColumnTable once a TableModel has been added to your main table and the main table has been added to a scroll pane. The result will be a scroll pane that contains two tables, only one of which can be scrolled horizontally. The following code was used to create the scroll pane pictured in the image below:

JTable table = new JTable(20, 10);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumnModel().getColumn(0).setPreferredWidth(50);
table.getColumnModel().getColumn(1).setPreferredWidth(100);
JScrollPane scrollPane= new JScrollPane( table );
FixedColumnTable fct = new FixedColumnTable(2, scrollPane);

fixed-column-table1

You can access the fixed table that was created by using the getFixedTable() method. This will allow you to adjust the properties of the table if required.

You can change the model of the main table and the change will be reflected in the fixed table. However, you cannot change the structure of the model.

Get The Code

FixedColumnTable.java

Related Reading

Providing Custom Decorations in a Scroll Pane
How to Write a Property Change Listener
How to Write a Change Listener

48 Responses to “Fixed Column Table”

  1. aterai said

    That worked fine.
    I want to learn your code.
    Thank you!
    (a bit offtopic sorry for my english)

  2. Thomas said

    Hi!

    need your help! Is it possible to make a “fixed row table” with this class? I want to make a filter like “autofilter in excel”. In the first (fixed) row i will place comboboxes for filtering. Anything i build don´t work …

    Thank you!

  3. angelo rodelas said

    thank you very much! You are really good. thank you for sharing! I love you very much!
    thank you…

  4. Zing said

    What happens when the user sorts this table? How does the row header know that it has to match the same viewToModel mapping?

    • Rob Camick said

      Some people have strongly suggested that you should not attempt to share RowSorters. However some other people claim to have success with something like:


      FixedColumnTable fct = new FixedColumnTable(2, scrollPane);
      JTable fixed = fct.getFixedTable();
      table.setAutoCreateRowSorter(true);
      fixed.setRowSorter(table.getRowSorter());
      table.setUpdateSelectionOnSort(true);
      fixed.setUpdateSelectionOnSort(false);

      I”ll let you decide if this works for your situation.

  5. Martin said

    Hi,

    can anyone tell me how to implement this in an usual panel?
    I dont get it. I´m like a noooob-programmer..

    public class CoC extends JPanel {

    private JScrollPane jScrollPane = null;
    private JTable table = null;

    public CoC() {
    super();
    initialize();
    }

    private void initialize() {
    this.setSize(1017, 457);
    this.setLayout(null);
    this.add(getJScrollPane(), null);
    }

    private JScrollPane getJScrollPane() {
    if (jScrollPane == null) {
    jScrollPane = new JScrollPane();
    jScrollPane.setBounds(new Rectangle(19, 22, 982, 157));
    jScrollPane.setViewportView(getTable());
    }
    return jScrollPane;
    }

    private JTable getTable() {
    if (table == null) {
    table = new JTable();
    table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    table.getColumnModel().getColumn(0).setPreferredWidth(50);
    table.getColumnModel().getColumn(1).setPreferredWidth(100);
    JScrollPane scrollPane= new JScrollPane( table );
    FixedColumnTable fct = new FixedColumnTable(2, scrollPane);
    }
    return table;
    }
    }

    Why it doesnt work?
    There´s nothing wrong, i think.

    Greetings,
    Martin

    • Rob Camick said

      The basic code would be:

      JTable table = new JTable(...);
      JScrollPane scrollPane = new JScrollPane( table );
      FixedColumnTable fct = new FixedColumnTable(2, scrollPane);
      panel.add( scrollPane );

      • Martin said

        i think my post didn´t work…anyway. Here it is again:

        I got my Table, my Data, the Header and the right Scrollpane.
        But i cant scroll.

        maybe you know how to solve the problem?

        <greetings,
        Martin

      • Rob Camick said

        Look at the original code I posted and look at the “resize mode”.

  6. JavaNoob said

    Hi Rob,

    first of all, i wanna thank you for sharing this.. ..i love your fixedTable!!

    But what to do, when i wanna add a row (or delete a row) when a button is clicked.

    Usually i got my table and my model.

    then i start the event (by buttonclick) and do sth like this:

    Object addRow[][] = {{ "1", "2", "3", //fixed
    "1", "1", "1", "1" //main}}};

    DefaultTableModel m = (DefaultTableModel) table.getModel();
    m.insertRow(table.getRowCount(), addRow );

    But how to get the Model from the fixedtable to add a row.

    thanks anyway!

    have a awesome weekend!

    • Rob Camick said

      Even though there are two tables there is still only a single model. So you just update the main model normallly:

      model.insertRow(0, new String[] {"1", "2", "a", "b", "c"});

  7. Martin said

    Hi Rob,

    i implemented your fixedColumnTable in my source.
    But how can i make the cells editable?

    greetings,
    Martin

  8. Jon said

    Hi Rob,

    I used your code and it is awesome, thank you. I am sorta new to Java so I hope my question is not to lame. Here goes, once I have locked the columns in the jtable how would I unlock them and remove them from the scroll pane? I have a table that I reuse and the first 4 columns change based on the users request. For instance the first two columns may have customer number and customer name but the user might request customer number, name and region. In this case I would go from two locked columns to three and vise versa.

    Thanks
    Jon

    • Rob Camick said

      Take a look at the source code of the class. Initially all the TableColumns are created in the main table. Then some columns are removed from the main table and added to the fixed table. You can simply do the opposite. Remove a TableColumn from the fixed and add it back to the main. Or (I have never tried it) but you should be able to reset the view of the scrollpane using:

      table.setModel(...);
      scrollPane.setViewportView( table );
      FixedColumnTable fct = new FixedColumnTable(?, scrollPane);

  9. Jo said

    Thanks for the code. It works great for me.
    One small issue is that when I try to copy and paste the values from the table, the first column value is always missing.
    Is there anything I can do to make this work?
    Thanks in advance.
    Jo

    • Jo said

      With help from :
      http://stackoverflow.com/questions/4671657/how-to-copy-content-of-the-jtable-to-clipboard
      I was able to copy the values and since the fixed table is in sync with the main table, I get both the fixed table cell and the main table cell on selection which is very nice.

      One remaining issue is that, when I use the code from the link, if I choose to select only values from the first (fixed) column, the copy action is not trapped.

      I tried:
      fixedTableColumn.getFixedTable().registerKeyboardAction(this,”Copy”,copy,JComponent.WHEN_FOCUSED);
      fixedTableColumn.getMainTable().registerKeyboardAction(this,”Copy”,copy,JComponent.WHEN_FOCUSED);

      but to no avail – the action just percolates through and instead copies text from another jtree node that I have highlighted. Selecting any one cell from the main table copies that cell and the value from the fixed column correctly.

      Any ideas?

      • Rob Camick said

        KeyEvents are only dispatched to components that have focus. By default I made the fixed table non-focusable. You should be able to change this by using:

        getFixedTable().setFocusable( true );

      • Jo said

        Great! that worked.

        Was there any particular reason you made it non-focusable? Just wondering if I may break something by changing this.

        Also, once I have selected a cell from the main table, if I go back and select a cell from the fixed column, the corresponding cells from the previously selected column(s) in the main table are automatically selected. How can I avoid that?

        Thanks again.

      • Rob Camick said

        The column header is not focusable so I thought the row header should be treated the same way.

        Sorry, I don’t understand the scenario of your second question. The two tables share the same selection model so the same row selection should result. Maybe you are using column selection? If you send a SSCCE through the “Contact Us” page I can take a look and see if I have any suggestions.

  10. Hi Rob,

    This is real great work. It perfectly solved my problem of having a Freeze Pane @ JTable. Very Good Work.

    I prefer to leave small additions I did..

    fixed = new JTable();
    fixed.setAutoCreateColumnsFromModel( false );
    fixed.setModel( main.getModel() );
    fixed.setSelectionModel( main.getSelectionModel() );
    fixed.setFocusable( false );
    fixed.setBackground(tableColor);
    fixed.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
    fixed.setDragEnabled(false);
    fixed.getTableHeader().setReorderingAllowed(false);
    fixed.setFont(new Font(“Tahoma”,Font.PLAIN,12));
    fixed.setRowHeight(20);

    // Remove the fixed columns from the main table
    // and add them to the fixed table

    for (int i = 0; i < fixedColumns; i++)
    {
    TableColumnModel columnModel = main.getColumnModel();
    TableColumn column = columnModel.getColumn( 0 );
    columnModel.removeColumn( column );
    fixed.getColumnModel().addColumn( column );
    }
    // Below code makes CheckBoxRenderer to be used on the Fixed JTable's first column header
    TableColumn checkBoxColumn = fixed.getColumnModel().getColumn(0);
    checkBoxColumn.setHeaderRenderer(new CheckBoxHeaderRenderer(this));
    // Below code makes Row Sort Option possible on the Main JTable
    fixed.setRowSorter(main.getRowSorter());
    main.setUpdateSelectionOnSort(true);
    fixed.setUpdateSelectionOnSort(false);

    Thanks much,
    John,

  11. Pooja said

    Hi, Thanx for this blog, It helped me… but while using it, I ‘m facing a problem with these freez coulmn table–>I have used auto sorter on both the tables, but they both sorters are working separately, means when I sort on 1 coulmn of freez table , all the related table column are only sorting together and rest table is not sorting up along with those freezed columns. This is happening either ways. So how to map both table’s original indexes in order to get proper sorting of full row together?

  12. Pooja said

    Hi Rob, thanx again man! you are a gem! I used the solution you have given to Zing, but here some weird problem is coming, sorting is working perfectly but my table view is not displaying correctly. some rows are displayed and some are hiding… Can you please help me out

  13. Pooja said

    Sorry for the 1 more new post, but as I couldn’t find any edit option so….
    well here found the problem route cause
    Exception in thread “AWT-EventQueue-0” java.lang.IndexOutOfBoundsException: Invalid range
    at javax.swing.DefaultRowSorter.rowsDeleted(Unknown Source)
    at javax.swing.JTable.notifySorter(Unknown Source)
    at javax.swing.JTable.sortedTableChanged(Unknown Source)
    at javax.swing.JTable.tableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableChanged(Unknown Source)
    at javax.swing.table.AbstractTableModel.fireTableRowsDeleted(Unknown Source)
    at javax.swing.table.DefaultTableModel.removeRow(Unknown Source)

    When I’m loading my table for refresh purpose I’m removing all the colmuns and re generating from Database so after passing the sorter of ‘main’ table to ‘ fixed’ table ( fixed.setRowSorter(genTable_jtbl.getRowSorter()) ) I’m getting above exception while removing data , and instead of my table is having more rows, its showing few only.

    for (int rowCount = tableModel.getRowCount() – 1; rowCount >= 0; rowCount–)
    {
    tableModel.removeRow(rowCount); // getting exception
    }

    Keen to hear from you Rob!

    • Rob Camick said

      I’ve never actually tried to reset the model when using the fixed column table. Maybe the easiest way is to just recreate the table and recreate the fixed column table from scratch.

  14. Anonymous said

    Hi Rob,

    Your solution has worked for me in fixing the first n columns of the table. However, being the perfectionist that I am I have noticed that the rows in the fixed portion don’t line up exactly with the rows in the scrollable portion. It looks like the rows are the same but the spacing between the header and the first row is slightly smaller in the fixed portion than it is in the scrollable portion. Am I the only one getting this? Perhaps it’s because I am working in Linux and switching to a windows look and feel could handle this?

    Thanks,
    Another Rob

    • Anonymous said

      Upon further inspection, I think the spacing is the same but the first row in the main table has a cell border along the top that is missing from the first row of the fixed table. If you scroll to the bottom of the table you can see this bit at the bottom of the fixed table. Any ideas?

      • Anonymous said

        Okay, so setting the viewport border for the scroll pane to null lines up the rows, but loses the border between the two tables. This is preferred to having the rows not line up, but ideally I would keep the thicker vertical border between the tables while having the rows line up.

  15. T said

    Hi,
    thank you for the code!

    If someone needs to make the fixed columns resizable, add this:

    fixed.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);

    fixed.getTableHeader().addMouseMotionListener(new MouseMotionListener() {

    @Override
    public void mouseDragged(MouseEvent e) {
    fixed.setPreferredScrollableViewportSize(fixed.getPreferredSize());

    }

    @Override
    public void mouseMoved(MouseEvent e) {
    }
    });

  16. Yanick said

    Hi,
    thank you for the code!! It’s work very well.

    I have a table with two first columns fixed and the third column, who is not fixed, is an editable CheckBox column. Everytime I select a row by clicking in the first or second column, the third column is checked or unchecked.

    It’s look like when mouse click in freeze column, it’s also click on the first non-freeze column as well.

    Can you help with with that bug?

    | Freeze Col1 | Freeze Col 2 | Checkbox Col 3 | Other cols… |

  17. Dana said

    Exactly what I needed.

    Thanks.

  18. Nilesh said

    This is well written code and easy to understand. Thanks for the post.
    When we resize the window such that only fixed columns are seen, there are no scroll bars. I understand horizontal scroll bars are not required, but vertical are. Am I correct?

    • Rob Camick said

      This is the way a scroll pane works (and has nothing to do with this class). Take the same table you are using and shrink the vertical height so that only the table header is displayed. You will see that both the horizontal and vertical scrollbars are not painted.

      • Shivi said

        Hi Rob,

        My table is having add/remove cols facility with other features like resizing,sorting cols,search cols,copying and export but once all cols are removed from main table and only fixed cols left the scrollbar become unavailable. This new requirement came and i tried but it didnt work (setting viewport of scroll) Please suggest a solution.

      • Rob Camick said

        This is just the way a JScrollPane works. The scrollbars appear based on the preferred size of the component added to the viewport. Since you have no columns in the table the preferred size is 0.

  19. Zing said

    Another interesting glitch with this one which I only noticed under Mac OS X, because here, the selection colour is different depending on whether the component has focus. hasFocus() on the main table will return false if you’re focused on the side table and vice versa.

    The workaround I’m using for this is to override both table’s hasFocus() methods to check the other table. But it’s fast becoming a pile of hacks and I wonder if there is some other way to do this.

  20. Anonymous said

    hello , how can I increase the width of the fixed columns ?

    • Rob Camick said

      The easiest way is to set the column size before creating the FixedColumnTable class. For example:

      JTable table = new JTable(...);
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      table.getColumnModel().getColumn(0).setPreferredWidth(250);
      JScrollPane scrollPane= new JScrollPane( table );
      FixedColumnTable fct = new FixedColumnTable(2, scrollPane);
      
    • Rob Camick said

      I updated my original answer. I think it should do what you want now.

      • Anonymous said

        Very well !
        I moved the statement to populate the table: table.setModel(new DefaultTableModel (date, column)); (date and column are 2 array), after the statement:FixedColumnTable fct = new FixedColumnTable(colonnefisse, scrollPane);
        This way eiesco to change the width of the fixed columns .

        Another question: Is it possible to merge two cells ?

  21. Anonymous said

    Hey ,,, I am having an issue … I have included a JTable header .. But some of the rows are hhidden behind the header. Kindly help

  22. Obinna Henry said

    Hi rob, thanks for this Fixed Column Class. Its really usefull. But i noticed that when you tab from the fixed table to the end of it, the focus moves back to the first cell instead of jumping to the next cell from the main table.

    • Rob Camick said

      This implementation uses two separate tables. The default tab Action for a JTable only tabs within cells of the table. If you want custom behaviour then you will need to add a custom tabbing action to the table. Check out the “Table Tabbing” blog entry which gives an example of how to create a custom Action. You would need to modify the logic to tab to the next component, when focus is on the last cell of a row.

Leave a comment