Java / Swing: JTextArea en un JScrollPane, ¿cómo prevenir el desplazamiento automático?

Aquí hay un fragmento de código ejecutable que muestra cuál es mi “problema”.

Tengo un JTextArea envuelto en un JScrollPane . Cuando cambio el texto de JTextArea , JScrollPane desplaza automáticamente hasta el final del texto y no quiero eso.

Aquí están mis requisitos:

  • la aplicación no debe desplazarse verticalmente de forma automática, pero …
  • el usuario debería ser capaz de desplazarse verticalmente
  • el usuario no debería poder desplazarse horizontalmente
  • la aplicación nunca debe desplazarse horizontalmente
  • JTextArea no debe ser editable

(por lo tanto, incluso si hay más texto de lo que puede caber horizontalmente, ni la aplicación ni el usuario deberían poder desplazarse horizontalmente. Mientras que verticalmente, solo el usuario debería poder desplazarse).

No sé cómo “arreglar” esto: ¿debería arreglarse esto usando los métodos JTextArea o JScrollPane ?

Tenga en cuenta que AFAICT no es un duplicado en absoluto: JTextPane evita el desplazamiento en el padre JScrollPane

Este es el ejemplo más divertido, cada 200 ms pone texto nuevo en JTextArea y puede ver que JScrollPane siempre se desplaza automáticamente hasta el final del texto.

 import javax.swing.*; import java.awt.*; import java.util.Random; public final class TextInScrollPane extends JFrame { private static final Random r = new Random( 42 ); public static void main( final String[] args ) { final JFrame f = new JFrame(); f.setDefaultCloseOperation( EXIT_ON_CLOSE ); f.setLayout(new BorderLayout()); final JTextArea jta = new JTextArea( "Some text", 30, 30 ); jta.setEditable( false ); // This must not be editable final JScrollPane jsp = new JScrollPane( jta ); jsp.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); jsp.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS ); f.add( jsp, BorderLayout.CENTER ); f.pack(); f.setLocationRelativeTo( null ); f.setVisible(true); final Thread t = new Thread( new Runnable() { public void run() { while ( true ) { try {Thread.sleep( 200 );} catch ( InterruptedException e ) {} final StringBuilder sb = new StringBuilder(); for (int i = 0; i < 50 + r.nextInt( 75 ); i++) { for (int j = 0; j < r.nextInt(120); j++) { sb.append( (char) 'a' + r.nextInt(26) ); } sb.append( '\n' ); } SwingUtilities.invokeLater( new Runnable() { public void run() { jta.setText( sb.toString() ); } } ); } } }); t.start(); } } 

¿Cómo establecer AUTO-SCROLLING de JTextArea en Java GUI?

http://download.oracle.com/javase/1.5.0/docs/api/javax/swing/text/DefaultCaret.html#NEVER_UPDATE

Tratar:

 JTextArea textArea = new JTextArea(); DefaultCaret caret = (DefaultCaret)textArea.getCaret(); caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); 

Esto debería evitar que el cursor desplace automáticamente el documento hacia abajo.

Respondiendo a mi propia pregunta: no estoy exactamente seguro de que esta sea la mejor manera de resolver mi problema, pero configurar el cursor de setCaretPosition(0) usando setCaretPosition(0) parece funcionar bien:

 jta.setText( sb.toString() ); jta.jta.setCaretPosition( 0 ); 

Te has encontrado con un comportamiento muy extraño en la implementación de las clases de documento. Uso un DefaultStyledDocument en un JTextPane dentro de un JScrollPane.

Ahora, aquí está lo extraño. Si actualizo el documento en la EventQueue (como lo hace al progtwigr un ejecutable para ejecutarlo más tarde), el panel de desplazamiento se desplaza automáticamente hasta el final.

Sin embargo, las clases de documentos aseguran que son seguros para subprocesos y realmente actualizables desde otro subproceso. Si me aseguro de actualizar en otro hilo que el de EventQueue todo funciona bien, pero el panel de desplazamiento NO se desplaza hasta el final.

No tengo ninguna explicación de por qué esto es así, no he buscado en la fuente Swing. He estado explotando esta “característica” desde 2006 y hasta ahora ha sido consistente 🙂