JTable with autoresize, horizontal scrolling and shrinkable first column
Asked Answered
K

2

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";
}
}
}
Kassie answered 19/3, 2013 at 12:15 Comment(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?Reproductive
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.Kassie
For better help sooner, post an SSCCE.Ejaculatory
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)Choline
C
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);
Choline answered 20/3, 2013 at 9:32 Comment(16)
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?Kassie
+1 .. I was trying to solve this question yesterday .. but could not figure out.Monocycle
@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?)Choline
Any idea if it would be possible to shrink the seconds column. Change the resize mode to ALL instead of SUBSEQUENTStole
@Stole the actual resize mode doesn't make much of a difference (afaics)Choline
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.Stole
first of all you guys are amazing. However, we're not quite there yet. I added the exact scenario to the original questionKassie
@Kassie 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 :-)Choline
Alright, thanks a lot! SwingX will be the choice for next projects then :)Kassie
@Kassie 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 - thanks for coming up with this :-)Choline
@Choline how can i make this but fit with height too!?Ship
@Ship don't understand what you are after - adjust rowHeights to fit the visible rect? No idea ...Choline
@Choline 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 +1Ship
@Ship use JXTable :) then you can configure it like xTable.setVisibleRowCount(20)Choline
@Choline 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 :)Ship
@Ship 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)Choline
A
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

Alduino answered 17/4, 2014 at 14:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.