问题
I have a JFrame
containing a JScrollPane
containing a JPanel
.
The JPanel
contains a bunch of JTextArea
s.
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 JTextArea
s 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?
回答1:
Your example is incorrectly synchronized in that it updates jTextArea2
on the initial thread. Note that JTextArea#setText() is no longer thread safe. The example below invokes EditorKit#read()
, as suggested here, to load the same 27 MB, 176 K line file examined here. This takes a few seconds, about about twice as long as the JTable
approach, but scrolling is comparable.
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();
});
}
}
回答2:
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 JScrollPane
s but the limit makes it unavoidable.
Moving the contents of the JPanel
into their own individual JscrollPane
s resolved the issue. The scrolling is still a bit laggy for reasons unknown to me.
来源:https://stackoverflow.com/questions/25674947/scrolling-limitation-with-jscrollpane-and-jviewport-maximum-sizes-smaller-than-c