Newsgroup: it.comp.java
Titolo: Scrolling JTextArea

Come faccio a fare in modo che una JTextArea scorra automaticamente quando
appendo delle stringhe (in particolare delle linee terminate con "\n") ?
Ho inserito la JTextArea in un JScrollPane ma non sempre scorre
automaticamente.... cosě ho pensato di fare nel modo seguente:
    myTextArea.append("...\n");
    int p = myScrollPane.getVerticalScrollBar().getMaximum();
    myScrollPane.getVerticalScrollBar().setValue(p);
perň la scrollbar non viene portata al massimo, ma bensě alla penultima
riga.... e se per caso scrivo piů di una riga quelle piů sotto non si
vedono... č come se la chiamata a getMaximum restituisse il valore massimo
che c'era prima di scrivere la stringa.
Esiste una soluzione migliore?

 appendo delle stringhe (in particolare delle linee terminate con "\n") ?
Credo ci sia un problema nel tuo codice perché lo scrolling automatico in
seguito ad append() è il comportamento di default di JTextArea quando è
inserito all'interno di un JScrollPane. Prova a compilare ed eseguire
l'esempio funzionante qui sotto, può servirti per individuare il problema.
Al limite postane parte del codice sul NG.
import java.awt.* ;
import java.awt.event.* ;
import javax.swing.* ;
class TTA extends JFrame implements ActionListener
{
 int lineCounter = 0 ;
 JTextArea jta ;
 JButton btn1, btn2 ;
 TTA()
 {
  super("Test JTextArea");
  jta = new JTextArea();
  JScrollPane jsp = new JScrollPane(jta);
  jsp.setPreferredSize(new Dimension(250,250));
  btn1 = new JButton("Add Text");
  btn2 = new JButton("Add a lot of Text");
  getContentPane().add(jsp, BorderLayout.NORTH);
  getContentPane().add(btn1, BorderLayout.CENTER);
  getContentPane().add(btn2, BorderLayout.SOUTH);
  btn1.addActionListener(this);
  btn2.addActionListener(this);
  addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
   {
    System.exit(0);
   }
  });
  pack();
  setVisible(true);
 }
 public void actionPerformed(ActionEvent ae)
 {
  if ( ae.getSource() == btn1)
  {
   jta.append("Test line #"+ lineCounter + "\n");
   lineCounter++ ;
  }
  else
  {
   for(int i=0; i<20; i++)
   {
    jta.append("A lot of text!\n");
   }
  }
 }
 public static void main(String[] args)
 {
  new TTA();
 }
}
Marco.
--
Per scrivermi direttamente elimina
__TOGLIMI__ dall'indirizzo e-mail.

 Al limite postane parte del codice sul NG.
[...]
Grazie Marco per avermi risposto.... purtroppo però anche nel tuo codice c'è
lo stesso problema che ho riscontrato io, e cioè:
tutto funziona bene (nel senso che la JTextArea scorre) appendendo stringhe,
ma se per caso io clicco all'interno della JTextArea (cioè faccio comparire
il cursore per inserire il testo) le successive append non fanno più
scorrere lo scrollpane. E questo succede anche mettendo la JTextArea non
editabile, nel qual caso il Caret non è visibile. Se invece si riporta il
cursore alla fine allora lo scrolla funziona...
Ho trovato una soluzione che pare funzionare... e cioè prima di fare delle
append riporto la posizione del cursore in fondo, in questo modo:
  jta.setCaretPosition(jta.getText().length());
Ciao :)
... e buon anno a tutto il NG.
Marco

   jta.setCaretPosition(jta.getText().length());
Già, non avevo considerato che l'interazione manuale con la JTextArea sposta
il Caret. In effetti questa è una feature dato che permette all'utente di
leggere o selezionare il contenuto della JTextArea anche se nel frattempo
viene aggiunto del testo.
Tuttavia la vera singolarità nel funzionamento della JTextArea è quella
segnalata, tra l'altro, nel bug #4201999 del Bug Database di Java. In
sostanza:
1) se l'append() viene richiamato dall'interno di un EventListener la
JTextArea scrolla automaticamente, sempre che il caret non sia stato
spostato nel frattempo con un clic del mouse. Se il caret, anche dopo essere
stato spostato, viene però riportato in fondo, anche manualmente, la
JTextArea riprende a scrollare automaticamente in seguito all'append().
2) se l'append() viene invece effettuato non dall'interno di un
EventListener, ma, ad esempio, da un thread separato, non avviene alcuno
scroll automatico.
Ecco un programma per testare questo comportamento:
import java.awt.* ;
import java.awt.event.* ;
import javax.swing.* ;
class TestTextArea extends JFrame implements ActionListener, Runnable
{
 int lineCounter = 0 ;
 JTextArea jta ;
 JButton btn1, btn2 ;
 JScrollPane jsp ;
 boolean threadEnabled = false;
 Thread mySelf;
 TestTextArea()
 {
  super("Test JTextArea");
  jta = new JTextArea();
  jta.setEditable(false);
  jsp = new JScrollPane(jta);
  jsp.setPreferredSize(new Dimension(250,250));
  btn1 = new JButton("Add Text");
  btn2 = new JButton("Enable Thread");
  getContentPane().add(jsp, BorderLayout.NORTH);
  getContentPane().add(btn1, BorderLayout.CENTER);
  getContentPane().add(btn2, BorderLayout.SOUTH);
  btn1.addActionListener(this);
  btn2.addActionListener(this);
  addWindowListener(new WindowAdapter()
  {
   public void windowClosing(WindowEvent e)
   {
    System.exit(0);
   }
  });
  pack();
  setVisible(true);
  mySelf = new Thread(this);
  mySelf.start();
 }
 void addText()
 {
  jta.append("Test line #"+ lineCounter + "\n");
  lineCounter++ ;
 }
 public void run()
 {
  try
  {
   while(true)
   {
    if (threadEnabled)
    {
     addText();
     //
     // Workaround: scommentare per attivarlo
     //
     // jta.setCaretPosition(jta.getDocument().getLength());
    }
    mySelf.sleep(1000);
   }
  }
  catch(InterruptedException e)
  {
  }
 }
 public void actionPerformed(ActionEvent ae)
 {
  if ( ae.getSource() == btn1)
  {
   addText();
  }
  else
  {
   threadEnabled = !threadEnabled ;
   btn2.setText(threadEnabled ? "Disable Thread" : "Enable Thread");
  }
 }
 public static void main(String[] args)
 {
  new TestTextArea();
 }
}
Sembra che questo comportamento sia una feature voluta dai progettisti Swing
(anche se la logica è un po' sfuggente), ma finisce per confondere il
programmatore e probabilmente verrà modificata nella prossima release. Uno
dei workaround proposti per questo comportamento è proprio quello che hai
trovato tu: spostare il Caret a fine testo dopo l'append(). Purtroppo in
questo modo si impedisce all'utente di leggere tranquillamente la parte già
scrollata della JTextArea, ma non si può avere tutto nella vita, giusto?


 scroll automatico.
Si, funziona esattamente così.... ma non capisco come fa la JTextArea a
capire da chi è stato invocato il suo metodo... Farà un check per
controllare che si tratti del thread per il dispatch degli eventi?
Comunque sei stato gentilissimo e ti ringrazio anche per avermi suggerito di
usare
jta.setCaretPosition(jta.getDocument().getLength());
al posto di
jta.setCaretPosition(jta.getText().getLength());
infatti avevo il timore che usando getText venisse creata un nuovo oggetto
String ogni volta, il che poteva diventare pesante se la textarea conteneva
molto testo.
Ciao ciao
Marco