【问题标题】:auto-scrolling TextView in android to bring text into view在android中自动滚动TextView以将文本显示在视图中
【发布时间】:2011-03-31 05:15:57
【问题描述】:

我有一个TextView,我正在向其中动态添加文本。

在我的main.xml 文件中,我设置了属性以使我的最大行数为 19 并且滚动条垂直。

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

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

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

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

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

【问题讨论】:

    标签: android scroll 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'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 之后。

    【讨论】:

    • 这是一个很好的建议,谢谢!但是,它与您发布的不完全一样,因为如果最近更改了文本,TextView.getLayout() 可以返回 null。与其他答案一样,解决方案是将整个滚动量计算和scrollTo() 调用放入mTextView.post(new Runnable() { ... 调用中。
    • 嗨@KNfLrPn .. 我可以使用你提到的滚动逻辑来完成我的任务吗?请漂亮! :D
    • 哈,我希望你做到了。我很确定在这个网站上发布代码意味着我们希望人们找到并使用它。
    • 我只是为 .getlayout 添加了一个空检查。效果很好
    • 使用 Layout.getHeight 计算滚动量更简单。也不要忘记考虑 TextView 的顶部和底部填充。 int scrollAmount = mTextView.getLayout().getHeight - (mTextView.getHeight() + mTextView.getPaddingTop() + mTextView.getPaddingBottom());
    【解决方案2】:

    在 XML 布局中的 TextView 上使用 android:gravity="bottom"。例如

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

    不要问我为什么会这样。

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

    【讨论】:

    • 对于我想要的,只是将信息不断地转储到 TextView 的底部,这是迄今为止最好的选择。有时很容易错过简单的选项,这真是令人惊讶。谢谢。
    • 它工作正常,但当它超过定义的大小时不显示可滚动条。如果你还想用条形符号滚动视图,那么在定义的 xml 中添加 android:scrollbars="vertical/horizo​​ntal"。
    • 我有一个案例,我想要滚动的视图是 LinearLayout 的最后一个视图,为了填充剩余空间,我使用了 android:layout_height="0dp" 和 android:layout_weight="1" ,并且在调用 setText() 时没有滚动到底部。用 android:layout_height="match_parent" 替换这些属性似乎有效,TextView 占据了未使用的空间。很奇怪。
    • 不适用于TextView 内的android.support.v4.widget.NestedScrollView
    • 我对这种方法的问题是它看起来很奇怪之前 TextView 填满,因为出现的文本都位于底部。我还没有找到解决方法,但在“连续记录”情况下,TextView 首先从顶部填充,然后在填充后连续滚动到底部,这似乎更自然。我想这是否会困扰人们将取决于它填满的速度和频率。
    【解决方案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);
            }
        });
    }
    

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

    【讨论】:

    • @Someone - 但 OP 没有使用 ScrollView - op 正在使用打开滚动的 TextView ==> “textview.setMovementMethod(new ScrollingMovementMethod()); 以允许滚动。”跨度>
    【解决方案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);
            }
        }
    }
    

    希望这会有所帮助。

    【讨论】:

    • 真的很有帮助。有答案的语气,但这项工作非常适合我
    • 如果 TextView 的 layout_width 设置为 wrap_content,这似乎不起作用,否则对我来说效果很好。
    【解决方案5】:

    如果您使用 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);
            }
        }
    }
    

    【讨论】:

    • 这是最好的答案!其他解决方案似乎效果不佳或并非一直如此。
    【解决方案6】:

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

    这个文件适合我

    【讨论】:

      【解决方案7】:
       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"/>
      

      【讨论】:

      • scrollview.post(new Runnable() { @Override public void run() { // TODO 自动生成的方法存根 scrollview.fullScroll(View.FOCUS_DOWN); } });
      • @An-droid 你可以设置gravity_layout="center"
      【解决方案8】:

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

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

      【讨论】:

      • 这(由于重力设置)似乎从下到上填满了 textview,所以它很少是预期的行为。
      【解决方案9】:

      我使用了一个小技巧......在我的情况下......

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

      【讨论】:

        【解决方案10】:
        // 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>
        

        【讨论】:

          【解决方案11】:

          为了避免创建我所做的虚拟 scoolview

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

          【讨论】:

            【解决方案12】:

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

            1. 最近更改文本时,getLayout 可能会抛出 NPE。
            2. scrollTo 应该改为 scrollBy
            3. 它不考虑文本视图或填充的相对位置。

            不用说这是一个很好的起点。这是我在文本观察器中实现的变体。在计算 delta 时,我们必须从底部减去 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);
                    }
            

            您可以玩弄延迟来改善您的用户体验。

            【讨论】:

              【解决方案13】:

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

              有一些问题,其中一些在cmets和其他答案中指出:

              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 方法的参数输入。

              我添加的行高的三分之一,将卷轴放高一点,使最后一行完全可见,正是因为实践与理论所倡导的不完全相符。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-10-07
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多