テキストを動的に追加するTextView
があります。
_main.xml
_ファイルには、最大行数19とスクロールバーを垂直にするプロパティが設定されています。
_.Java
_ファイルでは、textview.setMovementMethod(new ScrollingMovementMethod());
を使用してスクロールを許可しています。
スクロールはうまく機能します。 19行が使用され、さらに行が追加されるとすぐに、スクロールが開始されます。問題は、新しいテキストをスクロールして表示することです。
私はtextview.getScrollY()
の値を書き出しており、(手動でスクロールしてテキストの新しい行を追加しても)_0
_のままです。
したがって、textview.scrollTo(0, textview.getScrollY());
は何もしません。
textview
の垂直スクロール量を取得するために使用する別の方法はありますか?私が読んだすべてのことは、すべての意図と目的のために、私がやっていることは働くべきだと言う:/
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をインスタンス化した後。
つかいます Android:gravity="bottom"
は、XMLレイアウトのTextViewで。例えば。
<TextView
...
Android:gravity="bottom"
...
/>
なぜ機能するのか聞かないでください。
このメソッドの唯一の問題は、テキストビューを上にスクロールして戻したい場合、新しいテキストが挿入されるたびに再び下に「引き下げられ」続けることです。
これは、チャットテキストの最後までスクロールするために使用するものです...
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>
以前の回答は正しく機能しませんでしたが、これは機能します。
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);
}
}
}
お役に立てれば。
カーソル位置が設定された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);
}
}
}
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"/>
(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)
}
これは私のために働く
私はちょっとしたトリックを使いました...私の場合は....
<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>
// 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>
XMLレイアウトでの簡単な実装...これらの属性を使用してTextViewを定義します。
<TextView
...
Android:gravity="bottom"
Android:scrollbars="vertical"
/>
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で、スクロールを少し高くして、最後の行が完全に見えるようにします。これは、実際には理論が主張するものと正確に一致しません。
https://stackoverflow.com/a/7350267/4411645 私のために正確に動作しませんでした
良い出発点であることは言うまでもありません。ここに、テキストウォッチャー内の実装のバリエーションがあります。 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を改善できます。
私がしたダミーのスクロールビューの作成を避けるために
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);
}
});