title: JTable which is scrollable horizontally AND grows to fill parent container
body: <p>I want to put a <code>JTable</code> in a <code>JScrollPane</code> and want it to:</p>
  
  <ul>
  <li>be scrollable horizontally and vertically (only if it has content that makes it larger than parent container)</li>
  <li>automatically grow to fill parent container if needed (in both directions)</li>
  <li>only have either/both scroll bar(s) when needed</li>
  </ul>
  
  <p>The <code>JList</code> behaves exactly like this, btw.  But I have found no easy way to accomplish all goals simultaneously with a <code>JTable</code>.  The easy way to accomplish the first is to disable auto-resizing, but that then prevents the second from occurring.  I've come up with a very hacky solution which adds a component listener to the parent container of the <code>JScrollPane</code> and resizes the table when that container is resized.  However, I've had to add some "fudge factors" (see 4x comments) to the resize method, altering my height/width checks by small pixel amounts because of some inherent inaccuracy I guess.  Without these fudge factors, the scrollbars either appear too soon (before they are needed), or the very end of long table cells gets cut off even with the scrollbar present.  The worst part about these hacky fudge factors is that if you change up the look and feel of the GUI, then the entire solution stops working so cleanly (i.e. the fudge factors would need to change).  Which is obviously very far from ideal.</p>
  
  <p>There has got to be a cleaner way to accomplish this.  Can anybody help?  Run the attached code and drag the size of the entire frame around to see the table update.  This code uses the metal look and feel which seems to work on my Windows 7 machine with the fudge factors chose, but I wouldn't be surprised if it didn't work as cleanly on another OS.</p>
  
  <pre><code>import java.awt.BorderLayout;
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.EventQueue;
  import java.awt.event.ComponentAdapter;
  import java.awt.event.ComponentEvent;
  
  import javax.swing.BoxLayout;
  import javax.swing.JFrame;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.JTable;
  import javax.swing.ScrollPaneConstants;
  import javax.swing.UIManager;
  import javax.swing.table.DefaultTableModel;
  import javax.swing.table.TableCellRenderer;
  
  public class JTableScrollTest {
  
      private JFrame frame;
      private JPanel panel;
      private DefaultTableModel tableModel;
      private JTable table;
      private JScrollPane scrollPane;
  
      /**
       * Launch the application.
       */
      public static void main(String[] args) {
          try {
              UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
          } catch (Throwable e) {
              e.printStackTrace();
          }
  
          EventQueue.invokeLater(new Runnable() {
              public void run() {
                  try {
                      JTableScrollTest window = new JTableScrollTest();
                      window.frame.setVisible(true);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
              }
          });
      }
  
      /**
       * Create the application.
       */
      public JTableScrollTest() {
          initialize();
      }
  
      /**
       * Initialize the contents of the frame.
       */
      private void initialize() {
          frame = new JFrame();
          frame.setBounds(100, 100, 450, 300);
          frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  
          panel = new JPanel();
          frame.getContentPane().add(panel, BorderLayout.CENTER);
          panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
  
          scrollPane = new JScrollPane();
          panel.add(scrollPane);
  
          tableModel = new DefaultTableModel(new Object[]{"Stuff"},0);
          tableModel.addRow(new Object[]{"reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally long string"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"a"});
          tableModel.addRow(new Object[]{"LAST ITEM"});
          table = new JTable(tableModel);
          table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
          scrollPane.setViewportView(table);
  
          resizeTable();
          panel.addComponentListener(new ComponentAdapter() {
              @Override
              public void componentResized(ComponentEvent evt) {
                  resizeTable();
              }
          });
      }
  
      public void resizeTable() {
          int width = 0;
          int height = 0;
          for (int row = 0; row &lt; table.getRowCount(); row++) {
              TableCellRenderer renderer = table.getCellRenderer(row, 0);
              Component comp = table.prepareRenderer(renderer, row, 0);
              width = Math.max (comp.getPreferredSize().width, width);
              height += comp.getPreferredSize().height;
          }
          if (width &gt; panel.getWidth() - 2) { // fudge factor
              width += 4; // fudge factor
              scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
          } else {
              width = panel.getWidth();
              scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
          }
          if (height &gt; panel.getHeight() + 4) { // fudge factor
              height -= 26; // fudge factor
              scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
          } else {
              height = panel.getHeight();
              scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
          }
          table.setPreferredSize(new Dimension(width, height));
      }
  }
  </code></pre>