37

So, I have a TextView like so:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="140.7dp"
    android:id="@+id/terminalOutput"
    android:layout_marginBottom="0.0dp"
    android:scrollbars="vertical"
    android:scrollbarAlwaysDrawVerticalTrack="true"
    android:maxLines="8" />

I use it as a sort of running log, displayed to the user so they can monitor progress of a task that takes about 3 minutes. However, once I go over 8 lines, the text goes off screen. This is unintuitive to the user because they have no way of knowing that it went off screen, other than to manually poll by trying scroll down.

How can I make it so that every time I add some text to this TextView I make it scroll down as low as it can go?

Also, this is in Xamarin Android, but I don't think it's relevant. It's easy to translate between it and Java

Earlz
  • 62,085
  • 98
  • 303
  • 499

9 Answers9

78

From your Code, two steps have to do:

Step 1

Although you code in Xamarin Android, but as in Java in the xxxActivity.java for terminalOutput invoke must be like this

TextView outputText = (TextView) findViewById(R.id.terminalOutput);
outputText.setMovementMethod(new ScrollingMovementMethod());

Method setMovementMethod() by parameter ScrollingMovementMethod() is the gimmick.

Step 2

And in the layout as in Java-Android also in activity_xxx.xml for the TextView Declaration as above must have to add this

android:gravity="bottom"

When you add new line into the outputText like this:

outputText.append("\n"+"New text line.");

It will scroll to the last line automatically, and these all the magic for your need.

Hannes Hertach
  • 499
  • 5
  • 23
NOTSermsak
  • 356
  • 1
  • 8
  • 8
  • 1
    `ScrollingMovementMethod()` is a real gimmick, however is scrolls text without these Android "terminators" in top or bottom. – Neurotransmitter Mar 24 '16 at 17:37
  • 1
    Thank you this helped me alot!! i also have my layout_height set to android:layout_height="wrap_content" – Phil Aug 26 '16 at 12:48
  • This works, but after phone rotate from portrait to horizontal, the textview content is hidden. – Fei Sep 01 '16 at 21:03
  • Thank you. Doing outputText.setText instead of outputText.append, was the culprit for me. – gimmegimme Feb 19 '17 at 17:19
34

As per answer here Making TextView Scrollable in Android

You don't need to use a ScrollView actually.

Just set the

android:maxLines = "AN_INTEGER"

android:scrollbars = "vertical" properties of your TextView in your layout's xml file.

Then use:

yourTextView.setMovementMethod(new ScrollingMovementMethod());

in your code.

That will work..

Community
  • 1
  • 1
DarwinLouis
  • 1,111
  • 9
  • 15
  • I know the other answer is probably more general purpose and useful across more situations, but this answer was extremely simple and worked good enough for me – Earlz Nov 07 '13 at 14:56
  • 31
    This answer is not correct, the question refers to "automatically scroll". The answer is bellow, using gravity. – pocjoc Dec 07 '14 at 19:36
  • 2
    Be careful to have a non-zero height for the TextView. If you set `layout_height` to `0dp` as Android studio suggests for performance, auto-scroll will not work – Buddy Aug 01 '15 at 02:41
9

Had the same question. Tried several decisions from this and similar discussions, nothing worked. Solved it this way:

edtConsoleText.setSelection(edtConsoleText.getText().length());

after every .append() .

9

None of these answers were quite what I wanted, so I came up with this.

textView.append(log);

while (textView.canScrollVertically(1)) {
    textView.scrollBy(0, 10);
}

Do not forget to set movement method before scrolling

textView.setMovementMethod(new ScrollingMovementMethod());
dogauzun
  • 133
  • 2
  • 10
  • 2
    if you can control where the text is appended, like in your example, you can simply follow `textView.append(log)` with `textView.bringPointIntoView(textView.length())`. – Alex Cohn Jan 01 '21 at 20:41
  • @AlexCohn this is exactly what I was looking for. Text starts at the top and then scrolls down as it is appended to the bottom and would otherwise go off screen. – Louth Sep 21 '21 at 07:12
5

You can try in 2 solutions:

  1. Put TextView in a ScrollView

    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
            <TextView
            android:id="@+id/TextView01"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Your Text" >
        </TextView>
    </ScrollView>
    
  2. Use custom scroll TextView (same as traditional TextView but it can scroll)

    import android.content.Context;
    import android.graphics.Rect;
    import android.text.TextPaint;
    import android.util.AttributeSet;
    import android.view.animation.LinearInterpolator;
    import android.widget.Scroller;
    import android.widget.TextView;
    
     public class ScrollTextView extends TextView {
    
    
        // scrolling feature
        private Scroller mSlr;
    
        // milliseconds for a round of scrolling
        private int mRndDuration = 250;
    
        // the X offset when paused
        private int mXPaused = 0;
    
        // whether it's being paused
        private boolean mPaused = true;
    
        /*
         * constructor
         */
        public ScrollTextView(Context context) {
            this(context, null);
            // customize the TextView
            setSingleLine();
            setEllipsize(null);
            setVisibility(INVISIBLE);
        }
    
        /*
         * constructor
         */
        public ScrollTextView(Context context, AttributeSet attrs) {
            this(context, attrs, android.R.attr.textViewStyle);
            // customize the TextView
            setSingleLine();
            setEllipsize(null);
            setVisibility(INVISIBLE);
        }
    
        /*
         * constructor
         */
        public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            // customize the TextView
            setSingleLine();
            setEllipsize(null);
            setVisibility(INVISIBLE);
        }
    
        /**
         * begin to scroll the text from the original position
         */
        public void startScroll() {
            // begin from the very right side
            mXPaused = -1 * getWidth();
            // assume it's paused
            mPaused = true;
            resumeScroll();
        }
    
        /**
         * resume the scroll from the pausing point
         */
        public void resumeScroll() {
    
            if (!mPaused)
                return;
    
            // Do not know why it would not scroll sometimes
            // if setHorizontallyScrolling is called in constructor.
            setHorizontallyScrolling(true);
    
            // use LinearInterpolator for steady scrolling
            mSlr = new Scroller(this.getContext(), new LinearInterpolator());
            setScroller(mSlr);
    
            int scrollingLen = calculateScrollingLen();
            int distance = scrollingLen - (getWidth() + mXPaused);
            int duration = (new Double(mRndDuration * distance * 1.00000
                    / scrollingLen)).intValue();
    
            setVisibility(VISIBLE);
            mSlr.startScroll(mXPaused, 0, distance, 0, duration);
            mPaused = false;
        }
    
        /**
         * calculate the scrolling length of the text in pixel
         * 
         * @return the scrolling length in pixels
         */
        private int calculateScrollingLen() {
            TextPaint tp = getPaint();
            Rect rect = new Rect();
            String strTxt = getText().toString();
            tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
            int scrollingLen = rect.width() + getWidth();
            rect = null;
            return scrollingLen;
        }
    
        /**
         * pause scrolling the text
         */
        public void pauseScroll() {
            if (null == mSlr)
                return;
    
            if (mPaused)
                return;
    
            mPaused = true;
    
            // abortAnimation sets the current X to be the final X,
            // and sets isFinished to be true
            // so current position shall be saved
            mXPaused = mSlr.getCurrX();
    
            mSlr.abortAnimation();
        }
    
        @Override
        /*
         * override the computeScroll to restart scrolling when finished so as that
         * the text is scrolled forever
         */
        public void computeScroll() {
            super.computeScroll();
    
            if (null == mSlr)
                return;
    
            if (mSlr.isFinished() && (!mPaused)) {
                this.startScroll();
            }
        }
    
        public int getRndDuration() {
            return mRndDuration;
        }
    
        public void setRndDuration(int duration) {
            this.mRndDuration = duration;
        }
    
        public boolean isPaused() {
            return mPaused;
        }
     }
    

How to use:

ScrollTextView scrolltext = (ScrollTextView) findViewById(R.id.YourTextView);

(ScrollTextView class source: http://bear-polka.blogspot.com/2009/01/scrolltextview-scrolling-textview-for.html)

Quoc Truong
  • 545
  • 4
  • 9
4

From "How to scroll to bottom in a ScrollView on activity startup":

final ScrollView scrollview = ((ScrollView) findViewById(R.id.scrollview));
scrollview.post(new Runnable() {
  @Override
  public void run() {
    scrollview.fullScroll(ScrollView.FOCUS_DOWN);
  }
});

What you need is to put fullScroll() after append operation.

Community
  • 1
  • 1
Mansour
  • 1,787
  • 2
  • 20
  • 33
2

I am using Xmarin.

My solution is as many people mentioned, to textView inside a ScrollView.

If you want to see the new line at the bottom of the view, use

android:layout_gravity="bottom"

This keeps the new line at the bottom until you scroll the view. The view stays wherever you have scrolled.

No code is needed.

However, if you want the contents is always at bottom after appending, you need to add code after append():

myText.Append(...);

myscroll.FullScroll(FocusSearchDirection.Down);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Jim Oian
  • 21
  • 1
0

For me nothing worked perfectly except a combination of these two :-

  • Setting scroller.scrollTo(0, 70); in your java class.Use it before setting your textview but after appending that String. 52 dp is the height of my device.You can find it out using scroller.getBottom()); So I used 70 to adjust for the scroll view.
  • Setting android:scrollY="30dp" in your textview.
Surbhit Rao
  • 625
  • 6
  • 14
0

In my case, nothing worked at first because I was apparently attempting to make changes to the UI (auto scrolling up after adding text that is out of view), outside of the UI thread. The words would display but with no auto scroll. Correction, setText would work but not append.

My call for auto scroll was not in a UI thread because I coded the call to be made in response to a socket onMessage call; which I run on its own thread. So, I had to enclose my function call with the following code and everything worked.

 runOnUiThread(new Runnable() {
                 @Override
                 public void run() {

                     // Stuff that updates the UI

                 }
             });

I'm sure all of the methods would work in someway so long as you are running it on a UI thread. But, to achieve the autoscroll effect for a "chat room" i.e. new text at top of chat window but when amount of text is longer than the window - autoscroll up the new messages into view, i just used the ... fullScroll(View.FOCUS_DOWN) ... method.