在android中自动滚动TextView将文本带入视图

时间:2010-08-17 20:32:57

标签: android scroll textview

我有TextView我正在动态添加文字。

在我的main.xml文件中,我将属性设置为使我的最大线19和滚动条垂直。

.java文件中我使用textview.setMovementMethod(new ScrollingMovementMethod());来允许滚动。

滚动效果很好。一旦占用了19行,并且添加了更多行,它就会开始滚动。问题是,我希望新文本滚动到视图中。

我正在写出textview.getScrollY()的值,无论如何都会保持0(即使我手动向下滚动并添加新的文本行)。

因此textview.scrollTo(0, textview.getScrollY());对我没有任何作用。

我应该使用另一种方法来获取textview的垂直滚动量吗?我读过的所有内容都表明,无论出于什么意图和目的,我正在做的工作应该是:/

13 个答案:

答案 0 :(得分:70)

通过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's internal layout for the position
    // of the final line and then subtracting the TextView's 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。

答案 1 :(得分:52)

在XML布局中的TextView上使用android:gravity="bottom"。 E.g。

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

不要问我为什么会这样。

此方法的唯一问题是,如果您想要向后滚动文本视图,每次插入新文本时它都会再次“拉下”到底部。

答案 2 :(得分:28)

这是我用来一直滚动到聊天文字底部的内容......

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

编辑:这是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>

答案 3 :(得分:21)

以前的答案对我来说无法正常工作,但这有效。

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

希望这有帮助。

答案 4 :(得分:7)

如果使用设置了光标位置的Spannable或Editable字符串设置文本,TextView已经自动滚动。

首先,设置滚动方法:

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

答案 5 :(得分:4)

 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"/>

答案 6 :(得分:2)

我用了一个小技巧......就我而言......

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

答案 7 :(得分:2)

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

这适合我的文件

答案 8 :(得分:1)

// 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 :(得分:1)

简单实现...在XML布局中使用以下属性定义TextView:

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

答案 10 :(得分:0)

为了避免创建虚拟滚动视图我做了

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

答案 11 :(得分:0)

https://stackoverflow.com/a/7350267/4411645并不适合我

  1. 当文本最近更改时,getLayout可以抛出NPE。
  2. scrollTo应更改为scrollBy
  3. 不考虑textview或paddings的相对位置。
  4. 毋庸置疑,这是一个很好的起点。这是我在textwatcher中实现的变体。在计算delta时,我们必须从底部减去textView top,因为getLineTop()也返回相对于textView top的值。

            @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 :(得分:0)

基于KNfLrPn的答案,并纠正了该答案的一些问题,有一种解决方案在2019年的 Android Studio 3.4.2 中仍然有效,并且我已经在我正在开发的应用中进行了测试。

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

存在一些问题,其中一些在注释和其他答案中指出:

a)mTextView.getLayout().getLineTop(mTextView.getLineCount())给出了绑定错误。与mTextView.getLayout().getLineTop(L)等效的是mTextView.getLayout().getLineBottom(L-1)。所以我用一行代替了

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

b) max只是为了简化逻辑

c) scrollTo应该以一种线程形式出现在post方法内。

d)普通香草最后一行的底部显然不能完全解决问题,因为存在一个错误,即TextView的最后一行应完全出现在视图中,但出现了剪切关。因此,我向滚动添加了行高的1/3。可以校准,但是对我来说效果很好。

-/-

有时需要说出显而易见的内容:xy的值对应于scrollTo例程,该值与左侧不可见的文本中的像素数完全匹配(x)和顶部不可见的文本像素数(y)。这恰好对应于小部件scrollXscrollY属性的值。

因此,当一个人从文本的最后一行取y时,并且如果该值大于窗口小部件的高度,则它必须与需要调整的文本的像素数完全对应。隐藏,作为scrollTo方法的参数输入。

我添加的线条高度的三分之一,将滚动条稍高一点,使最后一行完全可见,这恰恰是由于实际操作与理论所倡导的不完全一致。