0
votes

If I want to prevent the JTextArea from scrolling to the bottom when I add text to the end, the solution is very simple: Just set the caret's update policy to DefaultCaret.NEVER_UPDATE before calling the JTextArea's append method. I am trying to do the same thing (load the text without scrolling), but for prepending text instead of appending text.

I have tried lots of things. One of them is this, but it doesn't work:

public void loadMoreUp(){
    caret = (DefaultCaret)ta.getCaret(); // ta is a JTextArea
    caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE); //doesn't work
    String s = "The new text\n";

    ta.setText(s + ta.getText()); // I have also tried with ta.getDocument().insertString(0,s,null)
}

The behavior I want is that "The new text" gets prepended to the top, but the JTextArea doesn't scroll up with it. "The new text" should not be visible unless the user manually scrolls up to see it.

How can I prepend text to the top of a JTextArea, without it scrolling up? My JTextArea is in a JScrollPane if that is relevant.

2
I couldn't reproduce this using either of the following lines of code: textArea.insert("Mein Hund frisst Nuesse\n", 0); textArea.setText("Mein Hund frisst Nuesse\n" + textArea.getText()); I tried to repro by typing letters followed by <enter> until I passed the size of the textArea and got the scrollbars to show. Then I clicked a button that ran that code and the viewport stayed, showing the last letters I typed. To see the prepended text, I had to manually scroll up. - MarsAtomic
Post your minimal reproducible example demonstrating what you are attempting to do and the problem that results. 1). Don't use setText(). 2) Do use Document.insertString(...). - camickr
@MarsAtomic My textArea has editable set to false - maybe that makes a difference? What I did is I scrolled to the middle of a large textArea, and then rightclicked a button which runs the code. What happens when I click the button is that everything gets shifted down a line. I am trying to find the easiest way to make it not shift. - john smith

2 Answers

0
votes

So it turns out that making your JTextArea uneditable is what's keeping the scrollbar at the top. I don't think prepending text is moving it -- it's more the case that it never moves at all.

If the goal is simply to keep the scrollbar scrolled all the way down the scrollpane, however, all you really have to do is set the caret position, and no one should be the wiser.

You can even set the caret position to some previous position by saving .getCaretPosition() and using that value later.

textArea.insert("Mein Hund frisst Nuesse\n", 0);
textArea.setCaretPosition(textArea.getDocument().getLength());

You can see my full example here, which you can use to reconcile against your implementation.

import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;
import java.awt.Dimension;
import javax.swing.JButton;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Foo extends JFrame implements ActionListener
{
  JTextArea textArea;
  JScrollPane scrollPane;
  JButton button;

  public Foo()
  {
    textArea = new JTextArea();
    textArea.setEditable(false);
    textArea.setText("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np\nq\nr\ns\nt\nu\nv\nw\nx\ny\nz\n");
    scrollPane = new JScrollPane(textArea);
    scrollPane.setPreferredSize(new Dimension(400, 200));

    button = new JButton("Prepend");
    button.addActionListener(this);

    this.add(button, BorderLayout.PAGE_END);
    this.add(scrollPane, BorderLayout.PAGE_START);
  }

  public void actionPerformed(ActionEvent e)
  {
    if(e.getSource() == button)
    {
      textArea.insert("Mein Hund frisst Nuesse\n", 0);
      textArea.setCaretPosition(textArea.getDocument().getLength());
      //textArea.setText("Mein Hund frisst Nuesse\n" + textArea.getText());
    }
  }

  public static void main(String[] args)
  {
    Foo f = new Foo();
    f.setPreferredSize(new Dimension(500, 300));
    f.pack();
    f.setVisible(true);
  }
}
0
votes

The following code seems to work:

public void loadMoreUp(){
    caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
    String s = "The new text\n";
    JScrollBar vbar = scrollPane.getVerticalScrollBar();
    int diff = vbar.getMaximum() - vbar.getValue();
    try{
        ta.getDocument().insertString(0, s, null);
    }
    catch(BadLocationException e){
        logger.error("Bad Location");
    }
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            vbar.setValue(vbar.getMaximum()-diff);
        }
    });
}

The basic idea is to remember the position relative to the END of the document (vbar.getMaximum() - vbar.getValue()), and then restore this value after prepending the text.

invokeLater is needed, otherwise getMaximum runs before its value gets updated. The drawback with this method is that invokeLater makes the text briefly flicker.