auto-scrolling TextView in android to bring text into view

Viewed 96752

I have a TextView that I'm dynamically adding text to.

in my main.xml file I have the properties set to make my max lines 19 and scrollbars vertical.

in the .java file I am using textview.setMovementMethod(new ScrollingMovementMethod()); to allow for scrolling.

The scrolling works great. As soon as 19 lines are taken up, and more lines are added it starts scrolling just as it should. The problem is, I want the new text to scroll into view.

I am writing out the value for textview.getScrollY() and it stays at 0 no matter what (even if I manually scroll it down and add a new line of text).

consequently textview.scrollTo(0, textview.getScrollY()); does nothing for me.

Is there another method I should be using to obtain the vertical scroll amount for the textview? Everything I've read says that for all intents and purposes, what I'm doing should be working :/

13 Answers

this is what I use to scroll all the way to the bottom of my chat text ...

public void onCreate(Bundle savedInstanceState)
{
    this.chat_ScrollView = (ScrollView) this.findViewById(R.id.chat_ScrollView);
    this.chat_text_chat = (TextView) this.findViewById(R.id.chat_text_chat);
}


public void addTextToTextView()
{
    String strTemp = "TestlineOne\nTestlineTwo\n";

    //append the new text to the bottom of the TextView
    chat_text_chat.append(strTemp);

    //scroll chat all the way to the bottom of the text
    //HOWEVER, this won't scroll all the way down !!!
    //chat_ScrollView.fullScroll(View.FOCUS_DOWN);

    //INSTEAD, scroll all the way down with:
    chat_ScrollView.post(new Runnable()
    {
        public void run()
        {
            chat_ScrollView.fullScroll(View.FOCUS_DOWN);
        }
    });
}

EDIT: here's the XML layout

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <!-- center chat display -->
    <ScrollView android:id="@+id/chat_ScrollView"
        android:layout_width="fill_parent" 
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:layout_alignParentLeft="true">

        <TextView android:id="@+id/chat_text_chat"
            android:text="center chat" 
            android:layout_width="fill_parent" 
            android:layout_height="fill_parent"
            android:singleLine="false" />

    </ScrollView>

</RelativeLayout>

Previous answers did not work correctly for me, this however works.

Create a TextView and do the following:

// ...
mTextView = (TextView)findViewById(R.id.your_text_view);
mTextView.setMovementMethod(new ScrollingMovementMethod());
// ...

Use the following function to append text to the TextView.

private void appendTextAndScroll(String text)
{
    if(mTextView != null){
        mTextView.append(text + "\n");
        final Layout layout = mTextView.getLayout();
        if(layout != null){
            int scrollDelta = layout.getLineBottom(mTextView.getLineCount() - 1) 
                - mTextView.getScrollY() - mTextView.getHeight();
            if(scrollDelta > 0)
                mTextView.scrollBy(0, scrollDelta);
        }
    }
}

Hope this helps.

(2017) using Kotlin:

// you need this to enable scrolling:
mTextView.movementMethod = ScrollingMovementMethod()
// to enable horizontal scrolling, that means word wrapping off:
mTextView.setHorizontallyScrolling(true)
...
mTextView.text = "Some long long very long text content"
mTextView.post {
     val scrollAmount = mTextView.layout.getLineTop(mTextView.lineCount) - mTextView.height
     mTextView.scrollTo(0, scrollAmount)
}

This works file for me

 scrollview=(ScrollView)findViewById(R.id.scrollview1); 
 tb2.setTextSize(30); 
 tb2=(TextView)findViewById(R.id.textView2);                 
 scrollview.fullScroll(View.FOCUS_DOWN);    

Or use this in TextView:

<TextView

android:id="@+id/tb2"
android:layout_width="fill_parent"
android:layout_height="225sp"
android:gravity="top"
android:background="@android:drawable/editbox_background"
android:scrollbars="vertical"/>

to avoid creating dummy scroolview I did

int top_scr,rec_text_scrollY;
top_scr=(int)rec_text.getTextSize()+rec_text.getHeight();
rec_text_scrollY=rec_text.getLineBounds(rec_text.getLineCount()-1, null)-top_scr;
    //repeat scroll here and in rec_text.post. 
    //If not scroll here text will be "jump up" after new append, and immediately scroll down
    //If not scroll in post, than scroll will not be actually processed
if(rec_text_scrollY>0)rec_text.scrollTo(0, rec_text_scrollY);
rec_text.post(new Runnable(){
    @Override
    public void run() {
        if(rec_text_scrollY>0)rec_text.scrollTo(0, rec_text_scrollY);
    }                   
});

Based on the answer from KNfLrPn , and correcting some issues from that answer, there is a solution that is still valid in Android Studio 3.4.2 in 2019 and that I've tested in my developing app.

    private void addMessage(String msg) {
        mTextView.append(msg + "\n");
        final int scrollAmount = 
          max(mTextView.getLayout().getLineBottom(
               mTextView.getLineCount()-1) - mTextView.getHeight(),0);
        mTextView.post(new Runnable() { 
        public void run() { 
            mTextView.scrollTo(0, mScrollAmount + 
               mTextView.getLineHeight()/3);
        }});
        mTextView.scrollTo(0, scrollAmount);
    }

There were some problems, some of them pointed out in the comments and other answers:

a) The line mTextView.getLayout().getLineTop(mTextView.getLineCount()) gives a bound error. The equivalent to mTextView.getLayout().getLineTop(L) is mTextView.getLayout().getLineBottom(L-1). So I've replaced it by the line

mTextView.getLayout().getLineBottom(
      mTextView.getLineCount()-1) - mTextView.getHeight()

b) The max is just for simplify the logic

c) scrollTo should appear within a post method in a kind of thread.

d) plain vanilla last line bottom doesn't solve the problem completely apparently because there is a bug that the last line of TextView that should appear completely in the view, appears cut off. So I add about 1/3 of the height of the line to the scroll. This can be calibrated, but it has worked well for me.

-/-

Sometimes what seems obvious needs to be said: The value of x and y corresponds to the scrollTo routine exactly matches the number of pixels in the text that is invisible on the left (x) and the number of pixels in the text that is invisible on the top (y). This corresponds exactly to the value of the widgets scrollX and scrollY properties.

Thus, when one takes the y from the last line of the text, and if this is a value greater than the widget's height, it must correspond exactly to the number of pixels of the text that needs to be hidden, which is entered as a parameter of the scrollTo method.

The third of the height of the line that I've added, put the scroll a little higher, making the last line fully visible, precisely because of practice does not correspond exactly to what the theory advocates.

Related