4

I have a jtable within a jscrollpane. I use the jgoodies Form Layout and have put the scrollpane in a row that is set to "pref". This is how the table looks like right now:

enter image description here

I am using swingx JXTable by the way and have set the visibleRowCount to 2. But only half a row is displayed.

While making the SSCCE I realised that its not working properly because I have a custom TextAreaRenderer. I think because of this renderer, the row height is not being calculated properly. I am trying to figure out why, in the meantime if you guys can spot it that would be great...

public static void main(String[] args) {
    JFrame frame=new JFrame();
    JPanel panel=new FormDebugPanel();
    frame.setContentPane(panel);
    FormLayout layout=new FormLayout("1dlu:grow,200dlu:grow,1dlu:grow",
            "10dlu,pref,5dlu,pref,10dlu");
    panel.setLayout(layout);
    String[] columns= {"1st Column","2nd Column","3rd Column"};
    String[][] data= {
            {"Sethu","Data","Something\nis\nhere"},
            {"Sethu","Data","Something\nis\nhere"},

    };
    JXTable table=new JXTable(data,columns);
    table.setVisibleRowCount(2);

    TableColumnModel columnModel=table.getColumnModel();
    columnModel.getColumn(2).setCellRenderer(new TextAreaRenderer());

    JScrollPane scrlPan=new JScrollPane(table);
    CellConstraints cc=new CellConstraints();
    panel.add(scrlPan, cc.xy(2, 2));
    panel.add(new JLabel("Label After Table"),cc.xy(2,4));
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.pack();
    frame.setVisible(true);
  }
}

And here's the renderer:

class TextAreaRenderer extends JTextArea implements TableCellRenderer {

private final DefaultTableCellRenderer adaptee = new DefaultTableCellRenderer();

/** map from table to map of rows to map of column heights */
private final Map<JTable,Map<Integer,Map<Integer,Integer>>> cellSizes = new HashMap<JTable,Map<Integer,Map<Integer,Integer>>>();

public TextAreaRenderer() {
    setLineWrap(true);
    setWrapStyleWord(true);
}

public Component getTableCellRendererComponent(//
        JTable table, Object obj, boolean isSelected, boolean hasFocus, int row, int column) {
    // set the colours, etc. using the standard for that platform
    adaptee.getTableCellRendererComponent(table, obj, isSelected, hasFocus, row, column);
    setForeground(adaptee.getForeground());
    setBackground(adaptee.getBackground());
    setBorder(adaptee.getBorder());
    setFont(adaptee.getFont());
    setText(adaptee.getText());

    // This line was very important to get it working with JDK1.4
    TableColumnModel columnModel = table.getColumnModel();
    setSize(columnModel.getColumn(column).getWidth(), 100000);
    int height_wanted = (int) getPreferredSize().getHeight();
    addSize(table, row, column, height_wanted);
    height_wanted = findTotalMaximumRowSize(table, row);
    if (height_wanted != table.getRowHeight(row)) {
        table.setRowHeight(row, height_wanted);
    }
    return this;
}

private void addSize(JTable table, int row, int column, int height) {
    Map<Integer,Map<Integer,Integer>> rows = cellSizes.get(table);
    if (rows == null) {
        cellSizes.put(table, rows = new HashMap<Integer,Map<Integer,Integer>>());
    }
    Map<Integer,Integer> rowheights =  rows.get(new Integer(row));
    if (rowheights == null) {
        rows.put(new Integer(row), rowheights = new HashMap<Integer,Integer>());
    }
    rowheights.put(new Integer(column), new Integer(height));
}

/**
 * Look through all columns and get the renderer. If it is also a
 * TextAreaRenderer, we look at the maximum height in its hash table for
 * this row.
 */
private int findTotalMaximumRowSize(JTable table, int row) {
    int maximum_height = 0;
    Enumeration<TableColumn> columns = table.getColumnModel().getColumns();
    while (columns.hasMoreElements()) {
        TableColumn tc = (TableColumn) columns.nextElement();
        TableCellRenderer cellRenderer = tc.getCellRenderer();
        if (cellRenderer instanceof TextAreaRenderer) {
            TextAreaRenderer tar = (TextAreaRenderer) cellRenderer;
            maximum_height = Math.max(maximum_height, tar.findMaximumRowSize(table, row));
        }
    }
    return maximum_height;
}

private int findMaximumRowSize(JTable table, int row) {
    Map<Integer,Map<Integer,Integer>> rows = cellSizes.get(table);
    if (rows == null)
        return 0;
    Map<Integer,Integer> rowheights = rows.get(new Integer(row));
    if (rowheights == null)
        return 0;
    int maximum_height = 0;
    for (Iterator<Map.Entry<Integer, Integer>> it = rowheights.entrySet().iterator(); it.hasNext();) {
        Map.Entry<Integer,Integer> entry = it.next();
        int cellHeight = ((Integer) entry.getValue()).intValue();
        maximum_height = Math.max(maximum_height, cellHeight);
    }
    return maximum_height;
}
}

When I use the TextAreaRenderer, then the setVisibleRowHeight() is not honoured correctly. I think it has something to do with not setting the row height properly in the renderer.

sethu
  • 8,181
  • 7
  • 39
  • 65
  • 2
    time for an sscce - seeing your comments, I'm not quite sure I understand what you are after :-) – kleopatra Oct 24 '11 at 10:28
  • 1
    didn't look at the renderer, so this is not related to it: in SwingX you _never_ implement a renderer (you'll loose all the niceties of automatic Highlighter/String representation, ..) instead, implement a ComponentProvider. The test package has examples of how-to do it, including a provider vending a JTextArea – kleopatra Oct 27 '11 at 09:48

3 Answers3

4

With JXTable, you can configure how many rows (or columns) to show initially:

    table.setVisibleRowCount(2)

Here's a quick snippet showing how-to set the initial size in terms of rows and dynamically update the pref height in a TableModelListener

    final JXTable table = new JXTable(3, 5);
    table.setVisibleRowCount(table.getRowCount());
    TableModelListener l = new TableModelListener() {

        @Override
        public void tableChanged(TableModelEvent e) {
            if (!(e.getType() == TableModelEvent.INSERT)) return;
            table.setVisibleRowCount(((TableModel) e.getSource()).getRowCount());
            RootPaneContainer frame = (RootPaneContainer) SwingUtilities.windowForComponent(table);
            ((JComponent) frame.getContentPane()).revalidate();
        }
    };
    table.getModel().addTableModelListener(l);
    Action insert = new AbstractAction("add row") {

        @Override
        public void actionPerformed(ActionEvent e) {
            ((DefaultTableModel) table.getModel()).addRow(new Object[] {});
        }
    };
    new Timer(500, insert).start();
    JComponent comp = new JPanel(new MigLayout());
    comp.add(new JScrollPane(table));
    JXFrame frame = wrapInFrame(comp, "visibleRowCount");
    show(frame, frame.getPreferredSize().width, frame.getPreferredSize().height * 4);
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • Thanks for that.. For me its more easier, the data in the table is loaded when the panel is constructed and will not change. So I will not need the listeners. But if I have wrapped the JXTable with a scrollpane, then along with setting the visibleRowCount() I will also need to set the size of the scrollpane correctly right? Could you help me with how do I set the size of the scrollpane? The logic for setting the the row count is simple. If the row count is less than 3 then setVisibleRowCount to row count. Else keep the setVisibleRowCount to 3 and scroll the remaining rows. – sethu Oct 24 '11 at 09:23
  • Sorry I take that back.. you dont set the size of the scrollpane, instead you set size of the row in the jgoodies form layout that I am using. I need to set the row size of the row that is housing this scrollpane + jxtable. How do I calculate that before the table is displayed though? – sethu Oct 24 '11 at 09:48
  • dont set fixed sizes, never-ever - let the layout handle it for you – kleopatra Oct 24 '11 at 10:05
2
setPreferredScrollableViewportSize(Dimension)

is something you should look into. and here is a good tutorial : How to Use Scroll Panes

COD3BOY
  • 11,964
  • 1
  • 38
  • 56
  • no, not much better than setXXSize .. especially not with components that support configuring logical sizes (like JXTable, f.i.) – kleopatra Oct 24 '11 at 07:37
1

I believe you can use preferredSize, minSize and maxSize properties of the JSCrollPane to achieve this. If you set preferredSize to the minimum size you want and the maxsize to the maximum size you want, it should work.

Low Flying Pelican
  • 5,974
  • 1
  • 32
  • 43
  • no, never-ever use any of the setXXSize, for reasons see f.i. http://stackoverflow.com/questions/7229226/avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swing – kleopatra Oct 24 '11 at 07:20