Автоматическая прокрутка TextView в android для отображения текста

У меня есть TextView, что я динамически добавляю текст в.

в моем файле main.xml у меня есть свойства, установленные для того, чтобы мои максимальные линии 19 и полосы прокрутки были вертикальными.

в файле .java я использую textview.setMovementMethod(new ScrollingMovementMethod());, чтобы разрешить прокрутку.

Прокрутка отлично работает. Как только будет снято 19 строк, добавлено больше строк, оно начнет прокручиваться так, как должно. Проблема в том, что я хочу, чтобы новый текст прокручивался в вид.

Я пишу значение для textview.getScrollY() и он остается на 0 независимо от того, что (даже если я вручную прокручу его и добавлю новую строку текста).

следовательно textview.scrollTo(0, textview.getScrollY()); ничего не делает для меня.

Есть ли другой метод, который я должен использовать для получения значения вертикальной прокрутки для TextView? Все, что я читал, говорит о том, что я должен делать все, что я делаю:/

Ответ 1

Взял некоторый рыть через источник TextView, но вот, что я придумал. Это не требует, чтобы вы обернули TextView в ScrollView и, насколько я могу судить, прекрасно работают.

// function to append a string to a TextView as a new line
// and scroll to the bottom if needed
private void addMessage(String msg) {
    // append the new string
    mTextView.append(msg + "\n");
    // find the amount we need to scroll.  This works by
    // asking the TextView internal layout for the position
    // of the final line and then subtracting the TextView height
    final int scrollAmount = mTextView.getLayout().getLineTop(mTextView.getLineCount()) - mTextView.getHeight();
    // if there is no need to scroll, scrollAmount will be <=0
    if (scrollAmount > 0)
        mTextView.scrollTo(0, scrollAmount);
    else
        mTextView.scrollTo(0, 0);
}

Пожалуйста, дайте мне знать, если вы найдете случай, когда это не удается. Я был бы признателен за возможность исправить ошибки в моем приложении;)

Изменить: я должен упомянуть, что я также использую

mTextView.setMovementMethod(new ScrollingMovementMethod());

после создания экземпляра моего TextView.

Ответ 2

Используйте android:gravity="bottom" в TextView в вашем XML-макете. Например.

<TextView
    ...
    android:gravity="bottom"
    ...
/>

Не спрашивайте меня, почему это работает.

Единственная проблема с этим методом заключается в том, что если вы хотите прокрутить резервную копию текста, он будет постоянно "сбрасываться" на дно каждый раз при вставке нового текста.

Ответ 3

это то, что я использую для прокрутки до конца текста моего чата...

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: здесь XML-макет

<?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>

Ответ 4

Предыдущие ответы не работали корректно для меня, однако это работает.

Создайте TextView и выполните следующие действия:

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

Используйте следующую функцию для добавления текста в 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);
        }
    }
}

Надеюсь, что это поможет.

Ответ 5

TextView уже имеет автоматическую прокрутку, если вы устанавливаете текст с помощью Spannable или Editable строк с установленной позицией курсора.

Сначала установите метод прокрутки:

mTextView.setMovementMethod(new ScrollingMovementMethod());

Затем для установки текста используйте следующее:

SpannableString spannable = new SpannableString(string);
Selection.setSelection(spannable, spannable.length());
mTextView.setText(spannable, TextView.BufferType.SPANNABLE);

SetSelection() перемещает курсор к этому индексу. Когда TextView установлен в SPANNABLE, он автоматически прокручивается, чтобы сделать курсор видимым. Обратите внимание, что это не нарисовать курсор, он просто прокручивает местоположение курсора в видимой части TextView.

Кроме того, поскольку TextView.append() обновляет текст до TextView.BufferType.EDITABLE и Editable реализует Spannable, вы можете сделать это:

mTextView.append(string);
Editable editable = mTextView.getEditableText();
Selection.setSelection(editable, editable.length());

Вот полная реализация виджета. Просто вызовите setText() или append() для этого виджета. Он немного отличается от приведенного выше, потому что он простирается от EditText, который уже заставляет его внутренний текст редактироваться.

import android.content.Context;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
import android.text.Selection;
import android.text.Spannable;
import android.text.method.MovementMethod;
import android.text.method.ScrollingMovementMethod;
import android.text.method.Touch;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;

public class AutoScrollTextView extends AppCompatEditText {
    public AutoScrollTextView(Context context) {
        this(context, null);
    }

    public AutoScrollTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public AutoScrollTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected boolean getDefaultEditable() {
        return false;
    }

    @Override
    protected MovementMethod getDefaultMovementMethod() {
        return new CursorScrollingMovementMethod();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        super.setText(text, type);
        scrollToEnd();
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        super.append(text, start, end);
        scrollToEnd();
    }

    public void scrollToEnd() {
        Editable editable = getText();
        Selection.setSelection(editable, editable.length());
    }

    @Override
    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
        super.onInitializeAccessibilityEvent(event);
        event.setClassName(AutoScrollTextView.class.getName());
    }

    /**
     * Moves cursor when scrolled so it doesn't auto-scroll on configuration changes.
     */
    private class CursorScrollingMovementMethod extends ScrollingMovementMethod {

        @Override
        public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
            widget.moveCursorToVisibleOffset();
            return super.onTouchEvent(widget, buffer, event);
        }
    }
}

Ответ 6

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

Или используйте это в 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"/>

Ответ 7

Я использовал небольшой трюк... в моем случае....

<FrameLayout
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:layout_below="@+id/textView"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_above="@+id/imageButton">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/scrollView"
        android:layout_gravity="left|top" >

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:inputType="textMultiLine"
            android:ems="10"
            android:text="@string/your_text" />
    </ScrollView>

</FrameLayout>

Ответ 8

// Layout Views
    private TextView mConversationView;
    private ScrollView mConversationViewScroller;

use it either in :

public void onCreate(Bundle savedInstanceState)
{
   //...blabla
   setContentView(R.layout.main); 
   //...blablabla
        mConversationView = (TextView) findViewById(R.id.in);       
        mConversationViewScroller = (ScrollView) findViewById(R.id.scroller);
}

or in "special" method e.g. 

public void initializeChatOrSth(...){
        //...blabla
        mConversationView = (TextView) findViewById(R.id.in);       
        mConversationViewScroller = (ScrollView) findViewById(R.id.scroller);
}

public void addTextToTextView()
{

             //...blablabla some code
                byte[] writeBuf = (byte[]) msg.obj;
          // construct a string from the buffer - i needed this or You can use by"stringing"
                String writeMessage = new String(writeBuf);
                mConversationView.append("\n"+"Me:  " + writeMessage);
                mConversationViewScroller.post(new Runnable()
                {
                    public void run()
                    {
                        mConversationViewScroller.fullScroll(View.FOCUS_DOWN);
                    }
                });
}

this one works fine, also we can maually scroll text to the very top - which is impossible when gravity tag in XML is used.

Of course XML (main) the texview should be nested inside scrollview , e.g:

<ScrollView
        android:id="@+id/scroller"
        android:layout_width="match_parent"
        android:layout_height="280dp"
        android:fillViewport="true"
        android:keepScreenOn="true"
        android:scrollbarStyle="insideInset"
        android:scrollbars="vertical" >

        <TextView
            android:id="@+id/in"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:keepScreenOn="true"
            android:scrollbars="vertical" >

        </TextView>
    </ScrollView>

Ответ 9

чтобы избежать создания фиктивного scroolview, я сделал

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);
    }                   
});

Ответ 10

Простая реализация... в вашем XML-макете определяет ваш TextView с этими атрибутами:

<TextView
    ...
    android:gravity="bottom"
    android:scrollbars="vertical"
/>

Ответ 11

fooobar.com/questions/22337/... не помог мне для

  • getLayout может вызывать NPE, когда текст был недавно изменен.
  • scrollTo следует изменить на scrollBy
  • Не учитывается относительное расположение текста или прокладок.

Нечего и говорить, что это хорошая отправная точка. Здесь моя вариация реализации, внутри textwatcher. Мы должны вычесть верхнюю часть textView снизу при вычислении дельта, потому что getLineTop() также возвращает значение относительно вершины textView.

        @Override
        public void afterTextChanged(Editable editable) {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    Layout layout = textView.getLayout();
                    if (layout != null) {
                        int lineTop = layout.getLineTop(textView.getLineCount());
                        final int scrollAmount = lineTop + textView.getPaddingTop()
                                + textView.getPaddingBottom() - textView.getBottom() + textView.getTop();
                        if (scrollAmount > 0) {
                            textView.scrollBy(0, scrollAmount);
                        } else {
                            textView.scrollTo(0, 0);
                        }
                    }
                }
            }, 1000L);
        }

Вы можете поиграть с задержкой, чтобы улучшить ваш ux.

Ответ 12

(2017) с использованием 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)
}

Этот файл работает для меня