Java Tips Weblog

  • Blog Stats

    • 2,568,724 hits
  • Categories

  • Archives

Table Column Adjuster

Posted by Rob Camick on November 10, 2008

JTable sets a default width for each TableColumn in the table. When designing the table you can alter this width by setting the preferred size of the table column. However this size is only going to be a best guess as to the real width of the column. The real width of the column will never be known until data has been loaded in the table. It would be nice to be able to alter the width of the columns so that all text in the cell is displayed.

The most accurate solution is to use the actual TableCellRenderer of each column in the JTable to calculate the preferred width of the cell based on the data contained in each cell. A basic implementation of this approach could be:

JTable table = new JTable( ... );
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

for (int column = 0; column < table.getColumnCount(); column++)
{
    TableColumn tableColumn = table.getColumnModel().getColumn(column);
    int preferredWidth = tableColumn.getMinWidth();
    int maxWidth = tableColumn.getMaxWidth();

    for (int row = 0; row < table.getRowCount(); row++)
    {
        TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
        Component c = table.prepareRenderer(cellRenderer, row, column);
        int width = c.getPreferredSize().width + table.getIntercellSpacing().width;
        preferredWidth = Math.max(preferredWidth, width);

        //  We've exceeded the maximum width, no need to check other rows

        if (preferredWidth >= maxWidth)
        {
            preferredWidth = maxWidth;
            break;
        }
    }

    tableColumn.setPreferredWidth( preferredWidth );
}

This simple implementation will only work for static data and does not include the column header when determining the width of the column. For more features you may want to consider using the TableColumnAdjuster described below. It provides more dynamic features for handling changes to the data in the TableModel.

In many cases a JTable will contain dynamic data. Maybe rows of data can be added or removed from the table, or maybe the user has the ability to modify the data in the cell of a table. Either of these changes may result in the preferred width of a column changing. In these cases you may want the ability to change the column widths after data is initially loaded and the ability to alter columns widths as the user changes the data in the table. The TableColumnAdjuster can handle these situations. That is not only can the TableColumnAdjuster be used to determine the initial columns widths, it can be configured to support dynamic calculation of column widths as the data changes.

To help the TableColumnAdjuster calculate the appropriate column width you can set the following properties:

  • Column Header Included – use the width of the column header
  • Column Data Included – loop through all the rows of the TableModel and find the cell renderer with the largest width
  • Only Adjust Larger – only reset the column width if the calculated value is greater than the current width of the column.

The maximum width calculated using the above properties will be used for the column width. To provide default column widths you might use code like the following which will generate the table shown in the image:


table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumnAdjuster tca = new TableColumnAdjuster(table);
tca.adjustColumns();

 

Above is an example of installing the column adjuster and using static adjustments. As mentioned earlier the TableColumnAdjuster also supports dynamic column adjusts using the following property:

  • Dynamic Adjustment – changes to the TableModel will cause the adjustments to occur automatically. Updating a table cell will cause the column to be adjusted. Adding/removing rows will cause all columns to be adjusted.

The user may find the dynamic adjustment a bit annoying if the width changes every time they type something, so we can also give the user to option to request column adjustments. This was done by adding Actions to the table which the user can invoke with the appropriate key stroke:

ActionKey Stroke
Adjust ColumnControl +
Adjust ColumnsControl Shift +
Restore ColumnControl –
Restore ColumnsControl Shift –
Toggle DynamicControl *
Toggle LargerControl /

There are four main methods that can be used to change the column widths:

  • Adjust Column – adjust the width of the specified column
  • Adjust Columns – adjust the width of all columns
  • Restore Column – restore the width of the specified column
  • Restore Columns – restore the width of all columns

Typically, you would only use the “Adjust Columns” method to initially set up the widths for all columns. The other methods are used to support the keyboard functionality.

Note, that this class was designed to be used with tables that use an auto resize mode of AUTO_RESIZE_OFF. With all other modes you are constrained, as the width of the columns must fit inside the table. So if you increase the width of one column, the widths of one or more of the other columns must decrease. Because of this, the resize mode of RESIZE_ALL_COLUMNS will work the best.

Hopefully the usage of the static methods along with the user controlled Actions will provide the flexibility needed to adjust the width of any column in any situation.

Get The Code

Table Column Adjuster.java
Table Column AdjusterDemo.java

Related Reading

Setting and Changing Table Column Widths
How to Use Key Bindings

88 Responses to “Table Column Adjuster”

  1. I have been looking for something like this for a while now.
    It works like a charm!!
    Thanks millions!!

  2. lefameuxmarc said

    Hello,
    i was looking also for such class.
    it fits perfectly my needs:)

    Thank you very much

  3. Lionel said

    Hello Rob,

    The code worked absolutely perfectly upon implementation in my project. Thank you kindly for your effort and time spent to solve the same problem all us developers were struggling with!

    I have placed your details in the script code in my application, for future reference.

    Once again, thank you very much!

    Regards,
    Lionel

  4. Nirav said

    Hi Rob,
    You have done nice job here…Table column width setting is a main prob. in application..
    You gave here nice solution..but i have found another solution which might be little simple to understand than this…
    Kindly see it and tell me if its correct or some modification needed..
    it is here
    http://niravjavadeveloper.blogspot.com/2011/05/resize-jtable-columns.html

  5. Anonymous said

    hiii
    i don’t have jre6 but i really need this class
    can i have a version with jdk1.5?
    thanks a lot!

  6. Hi Rob,

    Thanks again!!! :)

    I have one question – if we set the Table Header as null, i am getting exception

  7. Michael said

    Rob, wow great class! I have modified it for my use. Among other things, I added a tableContainer member var and have a property that allows for expanding to the full container width (like JScrollPane) if it’s not already wider than the container (tricky since I want it to fill the container exactly so I have to size each column carefully). I also had to change your Map to be just a Map (column number and width key value pairs), and added some functionality to manage the Map since it didn’t allow for the change in number of columns and my table does that.

    • Rob Camick said

      Glad you where able to customize the code for your requirement.

    • Matt said

      Hey Michael that is exactly what I am trying to do as well however it doesn’t appear to be working for me. Another problem may be the fact that I have hidden columns in my table, but I would think that the table.convertColumnIndexToView(e.getColumn()); would handle that with a -1. Would you mind sharing your code?

      • Michael said

        I have not tried hidden columns, but my table changes number of columns and all data as part of its main purpose. It does that with pretty much everything you do in the program. The table is really used for just display purposes, derived from something else. I don’t use convertColumnIndexToView.

        I added a JScrollPane tableContainer member variable and put it in the constructor. I did the same with an int tableContainerExclusionZone to limit what space the table can encroach on because I have a row header in the left column that needed to be accounted for. Row headers are really a pain I’ve found since they’re not really supported in Swing like a column header is. That’s beyond me since plenty of tables have a header down the left (spreadsheets anyone?).

        So after that, I made a new method adjustColumnsFillContainerWidth() that’s actually called at the end of the adjustColumns() method. That checks the current table width (that was made from normal adjustColumns()). Now the table would have already kept its max width if columns were added by the nature of how it works, but if columns decreased, it may no longer fill the space. So it calculates any leftover width that can be filled (container width minus table and exclusion zone), and expands it if needed by going through each column and adding width evenly. I have it getting all the widths to add for everything first, then does the adjustColumn() for each one.

        I also needed to update the map holding the column widths to remove map elements if the number of columns was reduced. The adding of columns already worked via the map’s put method called in updateTableColumn().

        Your hidden columns may require more work to make sure it fills the space. I’m not sure how it works but it may budget the space for that and not appear to take up the whole width. You would have to do an extra step to take that into account, on both adding and subtracting columns.

      • Dave said

        So from my understanding here, if the table width is smaller than the visible viewport, the table won’t be stretched by the TableColumnAdjuster. I’ll try to achieve the same as Michael, but I’ll try a simpler approach (since all I want is to stretch small tables): if any leftover width (table is added to a jscrollpane), set the table to JTable.AUTO_RESIZE_ALL_COLUMNS.
        What I am struggling at is how to know if there’s any leftover. Can you share the piece of code you wrote to achieve that?

      • Dave said

        Alright, found what I wanted on Rob’s answer here: http://stackoverflow.com/a/15240806/468508

      • Michael said

        Dave, yeah you found it. Actually for a minute I thought I was doing all this work when I could have just been using AUTO_RESIZE_ALL_COLUMNS on my table but no, that causes it to be useless when there are too many columns in a limited space. That’s what I was dealing with when I coded my stuff.

        Thanks for replying back here with what you found!

        “What I am struggling at is how to know if there’s any leftover.”

        Well in case anyone is doing what I’m doing, here is the beginning of my method adjustColumnsFillContainerWidth() where it checks if there is leftover, and the helper it uses. The tableContainerExclusionZone is a var I added for the width of the part of the container that table should not encroach on since I have a row header in the first column as I mentioned above. You don’t need it if you don’t have a row header like me. The 2 is there since I just found that 2 pixels weren’t being accounted for, maybe a column margin or something but I ended up just using 2 after having trouble fiddling with that.

        public void adjustColumnsFillContainerWidth(){
        	//only proceed if we're not filling the container's width currently
        	int tableWidth = getTableWidth();
        	int tableContainerWidthMinusExcl = tableContainer.getWidth() - tableContainerExclusionZone - 2;
        	if (tableWidth < tableContainerWidthMinusExcl){
        		//we have room to spare, need to expand into it
        		.... 
        	}
        }
                
        public int getTableWidth() {
        	int tableWidth = 0;
        	for (int i = 0; i < columnSizes.size(); i++)
        		tableWidth += (int) columnSizes.get(i);
        	return tableWidth;
        }
        
  8. Wolv said

    Exactly what i was looking for, thanks for sharing!

  9. Amani said

    Thaaaaaank alot for your help … this is what I was looking for :)

  10. Taying said

    YOU ROCK!

  11. Anonymous said

    Amazing dude… Nice work… with this class, adjust column width jtable is like piece of cake…. thanks^^
    posting

  12. Anonymous said

    Very nice! This helped me a lot!

  13. Anonymous said

    Great Job thanks..!!

  14. Rui Tomas said

    Excelent class…
    Should be included by default :)
    Thanks

  15. Anonymous said

    thanks too!
    Robert, googling for a solution just like that :-)

  16. hi there, i found this googling, i tried on my project but arent working i guess. the columns stay the same as default after the adjustcolumns call. this works only when you type directly on the cell? im filling the table with data retreived from a database. you have any tips to make this work in my project? thanks!

  17. Anonymous said

    Thank you!
    Are you releasing this with an open source license (BSD)?

  18. Anonymous said

    wow, someone finally plugged that hole in the JDK libraries :-)
    thank you very very much for sharing it.
    would you consider putting this code up on github or something so that people may submit any further enhancements they make to it?

  19. Govind Koranga said

    working excellent…

    thank you very much…

  20. Anonymous said

    Dude! You are awesome! Saved me a boat load of time and effort. Thanks!

  21. Anonymous said

    I think the link is dead, can someone upload a new version?

  22. Anonymous said

    Thank you! Great class as many have said. Any chance this makes it to github so stuff like filling the full container makes it into the shared codebase? And you never answered the license question. Currently I am not sure if it is even possible to use the code legally for anything.

  23. Arild said

    Very useful…. thanx alot… Havent found greater example.

  24. Gian said

    How can I implement it on custom coded model jTable?
    private String[][] data;
    private String[] header = {“Client Name”, “Contact No.”, “Gender”, “Unit”, “No. of Released”, “Date”};
    DefaultTableModel model = new DefaultTableModel(data, header);

  25. Zekeriya Bars said

    Thanks a lot for this very useful class !

  26. Anonymous said

    Thanks a mil..

  27. Anonymous said

    Helped me a lot.
    You’re awesome!

  28. Anonymous said

    Works perfect, really great, thanks a lot:)

  29. Thanks a lot for the code!

  30. Anonymous said

    Worked like a charm

  31. Anonymous said

    Works perfect, really great, thanks man!!!

  32. AJ said

    this works if my table only has strings. How can I use (or adjust) the class to work if my table also has an image.

    I had to add,

    table.getColumnModel().getColumn(0).setCellRenderer(table.getDefaultRenderer(ImageIcon.class));

    to be able to display the image but now I can not resize the column widths. Any help would be appreciated.

    • Rob Camick said

      Using Icons works for me. I usually override the getColumnClass() method to return the class type of the column so the table can use the default Icon renderer. However, setting the renderer manually should also work, just make sure you set the renderer before using the TableColumnAdjuster.

  33. Dhalwani Rohit said

    Thank you so much for the effort made to write this code. It was very very useful.

  34. Radu said

    very very usefull class. I could easily adjust it to do the following:
    1) to make it work with null header
    2) to make it also auto adjust row height

  35. thanks for your post. I won a lot of time!

  36. payam said

    wow… this is simply amazing. Thank you so much (Y)

  37. Tim said

    Thanks so much for making this, This is exactly what I was looking for. Awesome solution!

  38. Tim said

    Word of warning, this code causes race conditions if used in conjunction with a RowSorter. The TableColumnAdjuster may or may not receive the table update AFTER the table and sorter have been updated. When it doesn’t the row sorter will give outdated data and throw a IndexOutOfBoundsException. Recommendation: Migrate TableColumnAdjuster code into a child class of JTable, remove the add/remove listener code and overwrite tableChanged(e) to call parent method, then use the logic from the TableColumnAdjuster after. Thus ensures the table is already updated when the calls to the renderers for measuring the column widths are made. Tweak code as needed. Seems to work for me so far, but have not fully tested as of yet.

  39. Bfoniqi said

    Is there any way to adjust column’s width while typing?

    • Rob Camick said

      The adjustments are only done when the TableModel changes. When you type you are adding data to the editor, not the TableModel. The user could escape from the editor without ever saving the data.

  40. Rahul Kumar said

    Hi Rob ,THis class is fantastic class ,I am using this code in my application .Its working fine.But Keystroke concept is not working on Column Width.Can u tell me why its not working i.e Control + etc is not working on table to change the width

    • Rob Camick said

      I can’t guess what the problem might be. Did you add any debug code to see if the Actions are actually being invoked? If you email me a proper SSCCE that demonstrates the problem I can see if I notice a problem. Remember the SSCCE will only be a frame with a table and I should be able to compile and execute the code.

  41. Jens said

    Thanks for the good Helper Class.But i have a problem with it. I “installed” it and it works, but i have tables, which have the first columns removed. The first column have some data in my TableModel, which isn’t showing in the Table, because i removed the first column. Now when i add a new row with data to model the table will be refreshed and then your code is triggered, but i tries to resize the first hidden column i got ArrayOutOufBounds -1 Exception here:

    	public void adjustColumns()
    	{
    		TableColumnModel tcm = table.getColumnModel();
    
    		for (int i = 0; i < tcm.getColumnCount(); i++)
    		{
    			adjustColumn(i);
    		}
    	}
    

    What must i fix on your code, that it will work, if the first column is hidden / rmeoved? Thanks!

  42. Jens said

    good Job. I noticed the Bug for sorted Tables. yesterday i did not test it, but today i got the exception. then i treid your new version and worked!

  43. bm said

    Brilliant! Thank you! Thank you! Thank you!

  44. Thedath said

    But what if column header value is larger than the most longest row value in that column?

  45. Mayuri said

    Hey,
    This is really helpful to me.
    Thanks guys.

  46. Adrian said

    Thank you so much for your class! The default adjustColumns() was perfect for my use.

  47. wotcha said

    This is great but I have a couple of problems. I am using the solution on a table that contains list of results from a “SELECT * ” on a database, which returns 7 fields/columns. The first six columns are displayed but not last column. If I change the SELECT statement to remove first column then seventh column is displayed in Table but column header appears as …… I assume there is some kind of restriction on number of columns that can be displayed – I tried changing “this(table, 6);” to “this(table, 7);” in TableColumnAdjuster but still only displays 6 columns.

    Final question is can this useful solution work with some way of making overall size of table to be dynamic depending on size of Window.

    • Rob Camick said

      The TableColumnAdjuster does not control the number of column in the TableModel or the JTable. So the problem would be with the code creating the table. If “…” appears then it means the text can’t be displayed in the width calculated, which means you should determine the width based on the header value and the data. The size of the table is determined by the layout manager you are using and the width of the frame.

  48. Reblogged this on Samruddhi.

  49. Anonymous said

    Hi
    Iam new to programming and I need this code for my application but I just need to apply the changes by default I dont want to ask the user I just comment the if conditions and delete the end of the program can any one tell that if Iam in a right way?
    import java.awt.*;
    import java.awt.event.*;
    import java.beans.*;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.event.*;
    import javax.swing.table.*;

    /*
    * Class to manage the widths of colunmns in a table.
    *
    * Various properties control how the width of the column is calculated.
    * Another property controls whether column width calculation should be dynamic.
    * Finally, various Actions will be added to the table to allow the user
    * to customize the functionality.
    *
    * This class was designed to be used with tables that use an auto resize mode
    * of AUTO_RESIZE_OFF. With all other modes you are constrained as the width
    * of the columns must fit inside the table. So if you increase one column, one
    * or more of the other columns must decrease. Because of this the resize mode
    * of RESIZE_ALL_COLUMNS will work the best.
    */
    public class TableColumnAdjuster implements PropertyChangeListener, TableModelListener
    {
    private JTable table;
    private int spacing;
    private boolean isColumnHeaderIncluded;
    private boolean isColumnDataIncluded;
    private boolean isOnlyAdjustLarger;
    private boolean isDynamicAdjustment;
    private Map columnSizes = new HashMap();

    /*
    * Specify the table and use default spacing
    */
    public TableColumnAdjuster(JTable table)
    {
    this(table, 6);
    }

    /*
    * Specify the table and spacing
    */
    public TableColumnAdjuster(JTable table, int spacing)
    {
    this.table = table;
    this.spacing = spacing;
    setColumnHeaderIncluded( true );
    setColumnDataIncluded( true );
    setOnlyAdjustLarger( false );
    setDynamicAdjustment( false );
    installActions();
    }

    /*
    * Adjust the widths of all the columns in the table
    */
    public void adjustColumns()
    {
    TableColumnModel tcm = table.getColumnModel();

    for (int i = 0; i < tcm.getColumnCount(); i++)
    {
    adjustColumn(i);
    }
    }

    /*
    * Adjust the width of the specified column in the table
    */
    public void adjustColumn(final int column)
    {
    TableColumn tableColumn = table.getColumnModel().getColumn(column);

    if (! tableColumn.getResizable()) return;

    int columnHeaderWidth = getColumnHeaderWidth( column );
    int columnDataWidth = getColumnDataWidth( column );
    int preferredWidth = Math.max(columnHeaderWidth, columnDataWidth);

    updateTableColumn(column, preferredWidth);
    }

    /*
    * Calculated the width based on the column name
    */
    private int getColumnHeaderWidth(int column)
    {
    //if (! isColumnHeaderIncluded) return 0;

    TableColumn tableColumn = table.getColumnModel().getColumn(column);
    Object value = tableColumn.getHeaderValue();
    TableCellRenderer renderer = tableColumn.getHeaderRenderer();

    if (renderer == null)
    {
    renderer = table.getTableHeader().getDefaultRenderer();
    }

    Component c = renderer.getTableCellRendererComponent(table, value, false, false, -1, column);
    return c.getPreferredSize().width;
    }

    /*
    * Calculate the width based on the widest cell renderer for the
    * given column.
    */
    private int getColumnDataWidth(int column)
    {
    //if (! isColumnDataIncluded) return 0;

    int preferredWidth = 0;
    int maxWidth = table.getColumnModel().getColumn(column).getMaxWidth();

    for (int row = 0; row = maxWidth)
    break;
    }

    return preferredWidth;
    }

    /*
    * Get the preferred width for the specified cell
    */
    private int getCellDataWidth(int row, int column)
    {
    // Inovke the renderer for the cell to calculate the preferred width

    TableCellRenderer cellRenderer = table.getCellRenderer(row, column);
    Component c = table.prepareRenderer(cellRenderer, row, column);
    int width = c.getPreferredSize().width + table.getIntercellSpacing().width;

    return width;
    }

    /*
    * Update the TableColumn with the newly calculated width
    */
    private void updateTableColumn(int column, int width)
    {
    final TableColumn tableColumn = table.getColumnModel().getColumn(column);

    if (! tableColumn.getResizable()) return;

    width += spacing;

    // Don’t shrink the column width

    if (isOnlyAdjustLarger)
    {
    width = Math.max(width, tableColumn.getPreferredWidth());
    }

    columnSizes.put(tableColumn, new Integer(tableColumn.getWidth()));

    table.getTableHeader().setResizingColumn(tableColumn);
    tableColumn.setWidth(width);
    }

    /*
    * Restore the widths of the columns in the table to its previous width
    */
    public void restoreColumns()
    {
    TableColumnModel tcm = table.getColumnModel();

    for (int i = 0; i < tcm.getColumnCount(); i++)
    {
    restoreColumn(i);
    }
    }

    /*
    * Restore the width of the specified column to its previous width
    */
    private void restoreColumn(int column)
    {
    TableColumn tableColumn = table.getColumnModel().getColumn(column);
    Integer width = columnSizes.get(tableColumn);

    if (width != null)
    {
    table.getTableHeader().setResizingColumn(tableColumn);
    tableColumn.setWidth( width.intValue() );
    }
    }

  50. Todd Brunhoff said

    I spend my days writing c++ embedded systems, but on weekends I work on personal projects in java, et al. I wrote something like this because of my frustration with tables, but your full implementation reflects a much better understanding of all things JTable. Nicely done!

  51. Anonymous said

    thank you very much!

  52. Michael Jacob said

    Seems many people want a table to always fill the available space and have one column grow to take the slack. Me included.

    Here’s my solution to this: https://gist.github.com/HenryLoenwind/3d4601c0aa201a3a4f506fac64c8c5b6

    Note that is needs JTable.AUTO_RESIZE_OFF to NOT be set. It also ignores any maximum column width completely (I don’t need that), but it should be easy to add a list of maximum widths to use in place of the “99999” dummy.

  53. Anonymous said

    thanks, this posting still useful in 2022

Leave a reply to Anonymous Cancel reply