15

I have a simple Java question here. I want to auto text scroll to the beginning of the last line of a text area created using JTextArea. The amount of text per line of the text area is quite longer than the width of the text area.

Here is the code snippet I used to set that up.

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

The problem now is, with the above code, the default behavior is that the caret is auto positioned to the end of the document, as a result, the beginning part of the whole text area gets out of scope. I'd prefer the auto scroll to happen to the beginning of the last line in the document.

To make it clear, here are two screen shots,

What I want is the first one but what's happening is the second one.

What I want is this What I get is this

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
SSaikia_JtheRocker
  • 5,053
  • 1
  • 22
  • 41

1 Answers1

14

Simply move the caret to the correct location using getLineCount and getLineStartOffset after updating the text of the textarea.

Here is a working example illustrating your desired behaviour:

import java.util.List;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultCaret;

public class Test {

    private JFrame frame;
    private JTextArea ta;

    protected void initUI() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        ta = new JTextArea();
        DefaultCaret caret = (DefaultCaret) ta.getCaret();
        caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
        frame.add(new JScrollPane(ta));
        frame.setSize(400, 200);
        frame.setVisible(true);
        new UpdateText().execute();
    }

    class UpdateText extends SwingWorker<String, String> {

        @Override
        public String doInBackground() {
            for (int i = 0; i < 1000; i++) {
                publish("Hello-" + i);
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return null;
        }

        @Override
        public void process(List<String> chunks) {
            for (String s : chunks) {
                if (ta.getDocument().getLength() > 0) {
                    ta.append("\n");
                }
                ta.append(s);
            }
            try {
                ta.setCaretPosition(ta.getLineStartOffset(ta.getLineCount() - 1));
            } catch (BadLocationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        @Override
        public void done() {

        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new Test().initUI();
            }
        });
    }

}
Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
  • Cool solution! But I have a question here. Is there any way I could set the ta.setCaretPosition(ta.getLineStartOffset(ta.getLineCount() - 1)); only once instead of updating it every time, so that it remains valid for the entire application? – SSaikia_JtheRocker May 21 '12 at 19:04
  • Moreover, though you are using DefaultCaret caret = (DefaultCaret) ta.getCaret();caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE); Your code would work without these statements too? What is your view in this regard? – SSaikia_JtheRocker May 21 '12 at 19:09
  • @JtheRocker not that I know. The caret position keeps on moving as the text is updated. Therfore this means that its location needs to be recalculated everytime. You could probably extends JTextArea and override setText, by first calling super.setText and then position the caret. As for your second question, this is a question of desired behaviour and ergonomy. In your question that is what you asked for. Now, if you are looking in actually scrolling to the bottom left corner of the scrollpane, then you are looking for something else. If so, then consider posting another question on SO. – Guillaume Polet May 21 '12 at 19:12