webentwicklung-frage-antwort-db.com.de

auto-Scrolling TextView in Android, um Text sichtbar zu machen

Ich habe eine TextView, zu der ich dynamisch Text hinzufüge.

in meiner main.xml-Datei habe ich die Eigenschaften festgelegt, um meine maximalen Zeilen 19 und die Bildlaufleisten vertikal zu machen.

in der .Java-Datei verwende ich textview.setMovementMethod(new ScrollingMovementMethod());, um das Scrollen zu ermöglichen.

Das Scrollen funktioniert super. Sobald 19 Zeilen aufgenommen wurden und weitere Zeilen hinzugefügt wurden, beginnt das Scrollen wie gewünscht. Das Problem ist, ich möchte, dass der neue Text in die Ansicht verschoben wird.

Ich schreibe den Wert für textview.getScrollY() aus und er bleibt bei 0 (egal, selbst wenn ich ihn manuell nach unten scrolle und eine neue Textzeile hinzufüge).

folglich tut textview.scrollTo(0, textview.getScrollY()); nichts für mich.

Gibt es eine andere Methode, die ich verwenden sollte, um den vertikalen Bildlaufwert für textview zu erhalten? Alles, was ich gelesen habe, besagt, dass das, was ich tue, in jeder Hinsicht funktionieren sollte: /

42
Kyle

Ich habe etwas durch die TextView-Quelle gegraben, aber hier ist was ich gefunden habe. Es ist nicht erforderlich, dass Sie die TextView in eine ScrollView einbetten und funktioniert, soweit ich das beurteilen kann, perfekt.

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

Bitte lassen Sie mich wissen, wenn Sie einen Fall finden, in dem dies fehlschlägt. Ich würde mich freuen, wenn ich Fehler in meiner App beheben könnte;)

Edit: Ich sollte erwähnen, dass ich auch benutze

mTextView.setMovementMethod(new ScrollingMovementMethod());

nach dem Instantiieren meines TextView.

67
KNfLrPn

Verwenden Sie Android:gravity="bottom" für die TextView in Ihrem XML-Layout. Z.B.

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

Fragen Sie mich nicht, warum es funktioniert. 

Das einzige Problem bei dieser Methode ist, wenn Sie die Textansicht nach oben scrollen möchten, wird sie jedes Mal wieder nach unten gezogen, wenn neuer Text eingefügt wird.

49
Bryce Thomas

dies ist, was ich verwende, um ganz bis zum Ende meines Chat-Textes zu scrollen ...

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

BEARBEITEN: hier ist das 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>
27

Vorherige Antworten funktionierten für mich nicht korrekt, dies funktioniert jedoch.

Erstellen Sie eine TextView und führen Sie folgende Schritte aus:

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

Verwenden Sie die folgende Funktion, um Text an die TextView anzuhängen.

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

Hoffe das hilft.

21
user1230812

TextView verfügt bereits über einen automatischen Bildlauf, wenn Sie den Text mithilfe von Spannbaren oder Bearbeitbaren Zeichenfolgen mit der Cursorposition festlegen.

Legen Sie zunächst die Bildlaufmethode fest:

mTextView.setMovementMethod(new ScrollingMovementMethod());

Dann legen Sie den Text wie folgt fest:

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

SetSelection () verschiebt den Cursor auf diesen Index. Wenn eine Textansicht auf eine SPANNABLE gesetzt ist, wird automatisch gescrollt, um den Cursor sichtbar zu machen. Beachten Sie, dass dies nicht den Cursor zeichnet, sondern nur die Position des Cursors in dem sichtbaren Bereich der Textansicht durchläuft.

Da TextView.append () den Text auf TextView.BufferType.EDITABLE aktualisiert und Spannable implementiert, können Sie Folgendes tun:

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

Hier ist eine vollständige Widget-Implementierung. Rufen Sie einfach setText () oder append () für dieses Widget auf. Es ist etwas anders als oben, da es sich von EditText aus erstreckt, wodurch der interne Text bereits als bearbeitbar erzwungen wird.

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

Oder verwenden Sie dies 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"/>
4
Amitsharma

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

Das funktioniert Datei für mich

2
Almaz

Ich habe einen kleinen Trick benutzt ... in meinem Fall ....

<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

Einfache Implementierung ... in Ihrem XML-Layout definieren Sie Ihre TextView mit folgenden Attributen:

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

Basierend auf der Antwort von KNfLrPn und der Korrektur einiger Probleme aus dieser Antwort gibt es eine Lösung, die in Android Studio 3.4.2 im Jahr 2019 noch gültig ist, und dass ich habe in meiner entwicklungs app getestet.

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

Es gab einige Probleme, auf die einige in den Kommentaren und anderen Antworten hingewiesen wurden:

a) Die Zeile mTextView.getLayout().getLineTop(mTextView.getLineCount()) liefert einen gebundenen Fehler. Das Äquivalent zu mTextView.getLayout().getLineTop(L) ist mTextView.getLayout().getLineBottom(L-1). Also habe ich es durch die Zeile ersetzt

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

b) Das max dient nur zur Vereinfachung der Logik

c) scrollTo sollte innerhalb einer post -Methode in einer Art Thread auftreten.

d) Plain Vanilla letzte Zeile unten löst das Problem anscheinend nicht ganz, weil es einen Fehler gibt, der die letzte Zeile von TextView betrifft Sollte komplett in der Ansicht erscheinen, erscheint abgeschnitten. Also füge ich etwa 1/3 der Zeilenhöhe der Schriftrolle hinzu. Dies kann kalibriert werden, hat aber bei mir gut funktioniert.

-/-

Manchmal muss gesagt werden, was offensichtlich erscheint: Der Wert von x und y entspricht der Routine scrollTo und stimmt genau mit der Anzahl der Pixel im links unsichtbaren Text überein (x) und die Anzahl der Pixel im Text, die oben unsichtbar sind (y). Dies entspricht genau dem Wert der Eigenschaften widgets scrollX und scrollY.

Nimmt man also das y aus der letzten Textzeile und ist dies ein Wert, der größer als die Höhe des Widgets ist, muss er genau der Anzahl der Pixel des Texts entsprechen, die ausgeblendet werden müssen. wird als Parameter der Methode scrollTo eingegeben.

Das Drittel der Höhe der Linie, die ich hinzugefügt habe, setzt die Schriftrolle ein wenig höher und macht die letzte Linie vollständig sichtbar, gerade weil die Praxis nicht genau dem entspricht, was die Theorie befürwortet.

0
Paulo Buchsbaum

https://stackoverflow.com/a/7350267/4411645 hat für mich nicht gerade funktioniert

  1. das getLayout kann NPE werfen, wenn der Text kürzlich geändert wurde.
  2. scrollTo sollte in scrollBy geändert werden
  3. Die relative Position der Textansicht oder der Füllungen wird nicht berücksichtigt.

Es ist unnötig zu sagen, dass es ein guter Ausgangspunkt ist. Hier ist meine Variante der Implementierung innerhalb eines Textwatchers. Bei der Berechnung des Deltas müssen wir die textView-Spitze von unten subtrahieren, da getLineTop () auch den Wert relativ zur textView-Oberseite zurückgibt.

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

Sie können mit der Verzögerung herumspielen, um Ihren UX zu verbessern.

0
AA_PV

um das Erstellen eines Dummy-Bildlaufs zu vermeiden habe ich es getan

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