javaswingjpaneljscrollpanejviewport

Scrolling limitation with JScrollPane and JViewport maximum sizes smaller than contents


I have a JFrame containing a JScrollPane containing a JPanel. The JPanel contains a bunch of JTextAreas. I'm loading a lot of text into them (about 8k-10k characters).

The layout works fine, though the scrolling is a bit laggy.

The real issue is that it seems JPanel, JScrollPane and JViewport have a hard 32767 size limit, so when any JTextArea grows higher than that, it can't be scrolled any further to show the last 1/3 of the text.

Below you can see a minimal example for the problem. I used the NetBeans JFrame designer so it might be a bit lengthy but the only thing I have changed from the defaults is that the JTextAreas are direct children of the JPanel, the scrollbar policies, and slightly larger font size:

public class NewJFrame extends javax.swing.JFrame {

/**
 * Creates new form NewJFrame
 */
public NewJFrame() {
    initComponents();
}

/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    jScrollPane1 = new javax.swing.JScrollPane();
    jPanel1 = new javax.swing.JPanel();
    jTextArea1 = new javax.swing.JTextArea();
    jTextArea2 = new javax.swing.JTextArea();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    jScrollPane1.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

    jTextArea1.setColumns(20);
    jTextArea1.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N
    jTextArea1.setRows(5);

    jTextArea2.setColumns(20);
    jTextArea2.setFont(new java.awt.Font("Monospaced", 0, 14)); // NOI18N
    jTextArea2.setRows(5);
    jTextArea2.setText("Some long text...");

    javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
    jPanel1.setLayout(jPanel1Layout);
    jPanel1Layout.setHorizontalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(jPanel1Layout.createSequentialGroup()
            .addComponent(jTextArea1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jTextArea2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addGap(0, 0, 0))
    );
    jPanel1Layout.setVerticalGroup(
        jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(jPanel1Layout.createSequentialGroup()
            .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                .addComponent(jTextArea1, javax.swing.GroupLayout.DEFAULT_SIZE, 342, Short.MAX_VALUE)
                .addComponent(jTextArea2))
            .addGap(0, 0, 0))
    );

    jScrollPane1.setViewportView(jPanel1);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(jScrollPane1)
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 96, Short.MAX_VALUE)
    );

    pack();
}// </editor-fold>                        

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    final NewJFrame f = new NewJFrame();
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for(javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels())
            if("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
    }catch(ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }catch(InstantiationException ex) {
        java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }catch(IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }catch(javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            f.setVisible(true);
        }
    });
    StringBuilder txt = new StringBuilder();
    for(int i=0; i<10000; i++)
        txt.append(i).append("\n");
    f.jTextArea1.setText(txt.toString());
    txt = new StringBuilder();
    txt.append("JTextArea height: ").append(f.jTextArea1.getHeight()).append('\n');
    txt.append("JTextArea rows: ").append(f.jTextArea1.getRows()).append('\n');
    txt.append("JScrollPane height:").append(f.jScrollPane1.getHeight()).append('\n');
    txt.append("JViewport height:").append(f.jScrollPane1.getViewport().getHeight()).append('\n');
    txt.append("JPanel height:").append(f.jPanel1.getHeight()).append('\n');
    f.jTextArea2.setText(txt.toString());
}
// Variables declaration - do not modify                     
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea jTextArea1;
private javax.swing.JTextArea jTextArea2;
// End of variables declaration                   
}

If you run this and scroll to the bottom, you would expect to see the count reach 10 000 but it only goes to 1637, and you can see the top pixels from the next line just barely show up.

I have already tried to setMaximumSize and setSize on the JPanel, the JScrollPane and its JViewport but nothing has changed. I'm also somewhat confused that even though there's 10k lines of text, some of which can be scrolled far enough to be viewed, the getRows() and getSize() methods return the original values.

What is the right way to handle this situation when I want to have a scrollable JTextArea larger than 32767?


Solution

  • The problem had nothing to do with thread safety. After much digging, I found that the underlying window manager implementation for the Look And Feel of JPanel had a hardcoded 32767 size limit, so it didn't matter that it was sitting in a JScrollPane. The idea was to avoid a lot of extra managing of individual JScrollPanes but the limit makes it unavoidable.

    Moving the contents of the JPanel into their own individual JscrollPanes resolved the issue. The scrolling is still a bit laggy for reasons unknown to me.