5

I am having trouble creating a JTable with scrollbars. I want a JTable with 2 columns and no visible scrollbars.

If I enlarge one of the columns the scrollbars should become visible and the columns resize.

I followed this answer How to make JTable both AutoResize and horizontall scrollable? and works fine which basically comes down to:

JTable table = new JTable() {
  @Override
  public boolean getScrollableTracksViewportWidth() {
    return getPreferredSize().width < getParent().getWidth();
  }
};
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );

However, with this solution I cannot shrink the first column. Only if I enlarge the 2nd column and the scrollbars become visible I can shrink the first one.

The required behavior is that the 2 columns are automatically resizable. Meaning that the 1 column can shrink and afterwards extend without the scrollbars popping up. Only when extending one of the columns, so that the view should extend, the scrollbars should pop up.

A scenario:

  1. Shrink the 1st column -> 2nd one enlarges, no scrollbars
  2. Enlarge the 1st column -> 2nd one shrinks, still no scrollbars
  3. Enlarge the 2nd column -> 1 column stays the same, 2nd one enlarges and scrollbars appear

Any ideas on fixing this?

An SSCCE:

import javax.swing.JDialog;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import java.awt.BorderLayout;
import java.awt.Container;

public class TableTest {
public TableTest() {

JDialog mainDialog = new JDialog();
mainDialog.setResizable( true );
mainDialog.setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE );
Container contentPane = mainDialog.getContentPane();

JTable myTable = new JTable() {
  @Override
  public boolean getScrollableTracksViewportWidth() {
    return getPreferredSize().width < getParent().getWidth();
  }
};

myTable.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
myTable.setModel( new MyTableModel() );
JScrollPane scrollPane = new JScrollPane( myTable );
contentPane.add( scrollPane, BorderLayout.CENTER );

mainDialog.pack();
mainDialog.setVisible( true );
}

public static void main( String[] args ) {
new TableTest();
}

private class MyTableModel extends AbstractTableModel {

@Override public int getRowCount() {
  return 1;
}

@Override public int getColumnCount() {
  return 2;
}

@Override public Object getValueAt( int rowIndex, int columnIndex ) {
  return "ARandomValue";
}
}
}
Community
  • 1
  • 1
chillx
  • 53
  • 1
  • 4
  • Overriding scrollableTracksViewportWidth shouldn't have any effect on the resizability of columns. Is any of your code manipulating the minWidth, maxWidth, or resizable properties of the first TableColumn? – VGR Mar 19 '13 at 12:24
  • nope, there is no code that manipulates that. There is an AbstractTableModel that takes a list of values to populate the table. And futhermore the table is wrapped in a JScrollPane, thats it. – chillx Mar 19 '13 at 12:45
  • For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Mar 20 '13 at 02:33
  • seeing your edit: not supported in JXTable - when shrinking the first column, the others are extended, shrinking them back to the size to their initial would require additional logic (not sure how difficult that would be, the resize logic is very much a black box) – kleopatra Mar 20 '13 at 14:45

2 Answers2

7

It's not quite enough to override the getTracks method, you have to fool super's layout into doing the right-thingy if tracking:

JTable myTable = new JTable(10, 4) {
    private boolean inLayout;

    @Override
    public boolean getScrollableTracksViewportWidth() {
        return hasExcessWidth();

    }


    @Override
    public void doLayout() {
        if (hasExcessWidth()) {
            // fool super
            autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
        }
        inLayout = true;
        super.doLayout();
        inLayout = false;
        autoResizeMode = AUTO_RESIZE_OFF;
    }


    protected boolean hasExcessWidth() {
        return getPreferredSize().width < getParent().getWidth();
    }

    @Override
    public void columnMarginChanged(ChangeEvent e) {
        if (isEditing()) {
            // JW: darn - cleanup to terminate editing ...
            removeEditor();
        }
        TableColumn resizingColumn = getTableHeader().getResizingColumn();
        // Need to do this here, before the parent's
        // layout manager calls getPreferredSize().
        if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF
                && !inLayout) {
            resizingColumn.setPreferredWidth(resizingColumn.getWidth());
        }
        resizeAndRepaint();
    }

};

Might not be entirely complete (probably still isn't, even after the edit to take care of columnMarginChanged, copied from JXTable (of the SwingX project) which support that behaviour by an additional layout property

xTable.setHorizontalScrollEnabled(true);
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • looks great!! The first column is able to shrink correctly. Any idea if it would be possible to shrink the seconds column again if I enlarge the first column after shrinking it? Now the resized width of the seconds column remains. It should resize automatically again unless I enlarge it specifically. Or is the JXTable the way to go for this behaviour? – chillx Mar 20 '13 at 13:10
  • +1 .. I was trying to solve this question yesterday .. but could not figure out. – Amarnath Mar 20 '13 at 13:30
  • @user2186247 sorry, don't understand what exactly you want to achieve, so can't say if I implemented it in JXTable or not :-) Anyway, best to either edit your question with an explanation or maybe even post a new one (the original problem is solved, isn't it?) – kleopatra Mar 20 '13 at 13:44
  • 1
    `Any idea if it would be possible to shrink the seconds column`. Change the resize mode to `ALL` instead of `SUBSEQUENT` – camickr Mar 20 '13 at 16:21
  • @camickr the actual resize mode doesn't make much of a difference (afaics) – kleopatra Mar 20 '13 at 17:46
  • 1
    In the original question the OP only had two columns. You can't shrink the 2nd column when there is no horizontal scrollbar because SUBSEQUENT has no place to allocate the extra space. When you use ALL the the 1st column can increase as the 2nd shrinks. – camickr Mar 20 '13 at 21:06
  • first of all you guys are amazing. However, we're not quite there yet. I added the exact scenario to the original question – chillx Mar 21 '13 at 08:58
  • @chillx as partly mentioned in my comment to your edited answer yesterday: 2/3 are not supported - slightly astonished about 2, though, will open and hopefully fix an issue for JXTable (note: I maintain SwingX, not these snippets - if anybody has an idea, please contribute :-) – kleopatra Mar 21 '13 at 09:07
  • Alright, thanks a lot! SwingX will be the choice for next projects then :) – chillx Mar 21 '13 at 09:35
  • @chillx you are welcome :-) On checking again: 3 is fine for JXTable (that is extending the last column shows the scrollBar). As Rob noticed, shrinking the last doesn't work in the default autoresize (which is subsequentColumns). Opened [#1555 in SwingX issue tracker](http://java.net/jira/browse/SWINGX-1555) - thanks for coming up with this :-) – kleopatra Mar 21 '13 at 10:04
  • @kleopatra how can i make this but fit with height too!? – nachokk Aug 27 '13 at 19:17
  • @nachokk don't understand what you are after - adjust rowHeights to fit the visible rect? No idea ... – kleopatra Aug 28 '13 at 06:42
  • @kleopatra sorry , i mean to fit with the height of the jdialog, i override `JDialog#getPreferredSize()` and works but always show the scroll, i want for example 20 rows to fit without scroll, sorry for disturbing but this code works for the width :D +1 – nachokk Aug 28 '13 at 12:44
  • @nachokk use JXTable :) then you can configure it like `xTable.setVisibleRowCount(20)` – kleopatra Aug 28 '13 at 12:56
  • @kleopatra mm i replace it but it's give some error in other places with old stuff, so is not an option, but thank you anyway i appreciate your help if i have time i'll do some research resolving this :) – nachokk Aug 28 '13 at 13:21
  • @nachokk you can implement table.getPreferredScrollableViewportSize to return something reasonable (instead of the hard-coded dim) f.i. multiples of rowHeight (that's what JXTable is doing) – kleopatra Aug 28 '13 at 15:04
1

With the implementation of @kleopatra, I noticed that you get a scrollbar, when you reduce the size of a column and then increase it again just slightly (which happens quite often by accident). So I've slightly changed the code slightly:

protected boolean hasExcessWidth() {
    return getPreferredSize().width - getParent().getWidth() < 50;
}

This allows to slowly increase the size of a column without loosing the auto resize.

Not really sure yet if the magic "50" is a good measurement, but works quite well in initial tests