简体   繁体   English

JScrollPane和JViewport最大大小小于内容的滚动限制

[英]Scrolling limitation with JScrollPane and JViewport maximum sizes smaller than contents

I have a JFrame containing a JScrollPane containing a JPanel . 我有一个包含JScrollPaneJFrame其中包含一个JPanel The JPanel contains a bunch of JTextArea s. JPanel包含一堆JTextArea I'm loading a lot of text into them (about 8k-10k characters). 我正在加载大量文本(约8k-10k字符)。

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. 真正的问题是, JPanelJScrollPaneJViewport似乎有一个很大的32767大小限制,因此当任何JTextArea增长到高于此值时,它不能再滚动显示文本的最后1/3。

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 JTextArea s are direct children of the JPanel , the scrollbar policies, and slightly larger font size: 我使用NetBeans JFrame设计器,因此可能有点冗长,但我从默认设置中更改的唯一JTextAreaJTextAreaJPanel直接子项,滚动条策略和稍大的字体大小:

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. 如果你运行它并滚动到底部,你会看到计数达到10 000,但它只会达到1637,你可以看到下一行的顶部像素几乎没有出现。

I have already tried to setMaximumSize and setSize on the JPanel , the JScrollPane and its JViewport but nothing has changed. 我已经尝试在JPanelJScrollPane及其JViewport上设置setMaximumSizesetSize ,但没有任何改变。 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. 我也有些困惑,即使有10k行文本,其中一些可以滚动到足以被查看, getRows()getSize()方法返回原始值。

What is the right way to handle this situation when I want to have a scrollable JTextArea larger than 32767? 当我想要一个大于32767的可滚动JTextArea时,处理这种情况的正确方法是什么?

Your example is incorrectly synchronized in that it updates jTextArea2 on the initial thread . 您的示例未正确同步,因为它更新了初始线程上的jTextArea2 Note that JTextArea#setText() is no longer thread safe. 请注意, JTextArea#setText()不再是线程安全的。 The example below invokes EditorKit#read() , as suggested here , to load the same 27 MB, 176 K line file examined here . 下面的示例调用EditorKit#read() ,如此处所示 ,加载此处检查的相同27 MB,176 K行文件。 This takes a few seconds, about about twice as long as the JTable approach, but scrolling is comparable. 这需要几秒钟,大约是JTable方法的两倍,但滚动是可比的。

import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;

/**
 * @see https://stackoverflow.com/a/25691384/230513
 */
public class Test {

    private static final String NAME = "/var/log/install.log";

    private void display() {
        JFrame f = new JFrame("Test");
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        JTextArea text = new JTextArea(24, 32);
        try {
            text.read(new BufferedReader(new FileReader(NAME)), null);
        } catch (IOException ex) {
            ex.printStackTrace(System.err);
        }
        f.add(new JScrollPane(text));
        f.pack();
        f.setLocationRelativeTo(null);
        f.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            new Test().display();
        });
    }
}

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 . 经过大量挖掘后,我发现JPanel外观的底层窗口管理器实现有一个硬编码的32767大小限制,因此它与JScrollPane坐在一起并不重要。 The idea was to avoid a lot of extra managing of individual JScrollPane s but the limit makes it unavoidable. 我们的想法是避免对单个JScrollPane进行大量额外管理,但这个限制使其无法避免。

Moving the contents of the JPanel into their own individual JscrollPane s resolved the issue. JPanel的内容移动到他们自己的JscrollPane解决了这个问题。 The scrolling is still a bit laggy for reasons unknown to me. 由于我不知道的原因,滚动仍然有点滞后。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM