web-dev-qa-db-ja.com

AndroidでTextViewを表示するためのTextViewの自動スクロール

テキストを動的に追加するTextViewがあります。

_main.xml_ファイルには、最大行数19とスクロールバーを垂直にするプロパティが設定されています。

_.Java_ファイルでは、textview.setMovementMethod(new ScrollingMovementMethod());を使用してスクロールを許可しています。

スクロールはうまく機能します。 19行が使用され、さらに行が追加されるとすぐに、スクロールが開始されます。問題は、新しいテキストをスクロールして表示することです。

私はtextview.getScrollY()の値を書き出しており、(手動でスクロールしてテキストの新しい行を追加しても)_0_のままです。

したがって、textview.scrollTo(0, textview.getScrollY());は何もしません。

textviewの垂直スクロール量を取得するために使用する別の方法はありますか?私が読んだすべてのことは、すべての意図と目的のために、私がやっていることは働くべきだと言う:/

47
Kyle

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をインスタンス化した後。

72
KNfLrPn

つかいます Android:gravity="bottom"は、XMLレイアウトのTextViewで。例えば。

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

なぜ機能するのか聞かないでください。

このメソッドの唯一の問題は、テキストビューを上にスクロールして戻したい場合、新しいテキストが挿入されるたびに再び下に「引き下げられ」続けることです。

54
Bryce Thomas

これは、チャットテキストの最後までスクロールするために使用するものです...

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

以前の回答は正しく機能しませんでしたが、これは機能します。

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

お役に立てれば。

22
user1230812

カーソル位置が設定された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);
        }
    }
}
7
tsmigiel
 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"/>
4
Amitsharma

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

これは私のために働く

2
Almaz

私はちょっとしたトリックを使いました...私の場合は....

<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>
2
Stephan
// 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>
1
Maciek

XMLレイアウトでの簡単な実装...これらの属性を使用してTextViewを定義します。

<TextView
    ...
    Android:gravity="bottom"
    Android:scrollbars="vertical"
/>
1
noelicus

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を追加します。これは調整できますが、私にとってはうまくいきました。

-/-

明らかなことを言う必要がある場合があります:xおよびyの値は、scrollToルーチンに対応し、左側に表示されていないテキストのピクセル数と正確に一致します。 (x)および上部に表示されていないテキストのピクセル数(y)。これは、ウィジェットのscrollXおよびscrollYプロパティの値に正確に対応します。

したがって、テキストの最後の行からyを取得し、これがウィジェットの高さより大きい値である場合、非表示にする必要があるテキストのピクセル数に正確に対応する必要があります。 scrollToメソッドのパラメーターとして入力されます。

追加した行の高さの3分の1で、スクロールを少し高くして、最後の行が完全に見えるようにします。これは、実際には理論が主張するものと正確に一致しません。

0
Paulo Buchsbaum

https://stackoverflow.com/a/7350267/4411645 私のために正確に動作しませんでした

  1. getLayoutは、テキストが最近変更されたときにNPEをスローできます。
  2. scrollToをscrollByに変更する必要があります
  3. テキストビューやパディングの相対的な位置は考慮されません。

良い出発点であることは言うまでもありません。ここに、テキストウォッチャー内の実装のバリエーションがあります。 getLineTop()もtextView topに関連する値を返すため、デルタを計算するときに、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を改善できます。

0
AA_PV

私がしたダミーのスクロールビューの作成を避けるために

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