Android 如何获取TextView的行数?

Android 如何获取TextView的行数?,android,textview,Android,Textview,我想获得文本视图的行数 textView.setText("Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............") textView.getLineCount()始终返回零 然后我也试过: ViewTreeObserver vto = this.textView.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayou

我想获得文本视图的行数

textView.setText("Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............")
textView.getLineCount()始终返回

然后我也试过:

ViewTreeObserver vto = this.textView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {

    @Override
    public void onGlobalLayout() {
        ViewTreeObserver obs = textView.getViewTreeObserver();
        obs.removeGlobalOnLayoutListener(this);
        System.out.println(": " + textView.getLineCount());

    }
});
它返回精确的输出

但这只适用于静态布局

当我动态膨胀布局时,这不再起作用

如何在文本视图中找到行数?

如本文所述

getLineCount()
仅在 布局通行证


这意味着您需要在调用
getLineCount()
方法之前先呈现
TextView

我认为这个问题的关键是人们希望能够提前找到TextView的大小,以便能够动态地调整它的大小,以很好地适应文本。一个典型的用法可能是创建聊天泡泡(至少这是我正在研究的)

我尝试了几种解决方案,包括使用getTextBounds()和measureText(),如前所述。不幸的是,这两种方法都有点不精确,无法解释换行和未使用的线空间。所以,我放弃了这种方法

这就剩下了getLineCount(),它的问题是,在getLineCount()给出行数之前,必须“呈现”文本,这就造成了鸡和蛋的情况。我读过各种涉及监听器和布局的解决方案,但我不相信没有更简单的解决方案

在摆弄了两天之后,我终于找到了我要找的东西(至少它对我有用)。这一切都归结为“呈现”文本意味着什么。这并不意味着文本必须出现在屏幕上,只是它必须准备好在内部显示。当直接调用invalidate()或间接调用TextView上的setText()时,就会发生这种情况,因为视图的外观已更改,因此会为您调用invalidate()

无论如何,这里是关键代码(假设您已经知道talk bubble的线宽和基于字体的单行高度):

总之,就是这样。下一次重画将为您的文本生成一个特制的气泡

注意:在实际的程序中,我使用一个单独的气泡来确定文本行,这样我就可以根据长度和宽度动态调整真实气泡的大小。这使我可以从左到右收缩气泡,以获得简短的语句等


享受吧

ViewTreeObserver
不太可靠,尤其是在使用动态布局(如
ListView
)时

让我们假设:
1.您将根据
TextView
的行进行一些工作
2.这项工作不是很紧急,可以以后再做

以下是我的解决方案:

public class LayoutedTextView extends TextView {

        public LayoutedTextView(Context context) {
                super(context);
        }

        public LayoutedTextView(Context context, AttributeSet attrs) {
                super(context, attrs);
        }

        public LayoutedTextView(Context context, AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
        }

        public interface OnLayoutListener {
                void onLayouted(TextView view);
        }

        private OnLayoutListener mOnLayoutListener;

        public void setOnLayoutListener(OnLayoutListener listener) {
                mOnLayoutListener = listener;
        }

        @Override
        protected void onLayout(boolean changed, int left, int top, int right,
                        int bottom) {
                super.onLayout(changed, left, top, right, bottom);

                if (mOnLayoutListener != null) {
                        mOnLayoutListener.onLayouted(this);
                }
        }

}
用法:

LayoutedTextView tv = new LayoutedTextView(context);
tv.setOnLayoutListener(new OnLayoutListener() {
        @Override
        public void onLayouted(TextView view) {
                int lineCount = view.getLineCount();
                // do your work
        }
});

我能够使用
post
使
getLineCount()
不返回0,如下所示:

textview.setText(“Some text”);
textview.post(new Runnable() {
    @Override
    public void run() {
        int lineCount = textview.getLineCount();
        // Use lineCount here
    }
});
fun TextView.getTextLineCount(text: String, lineCount: (Int) -> (Unit)) {
    val params: PrecomputedTextCompat.Params = TextViewCompat.getTextMetricsParams(this)
    val ref: WeakReference<TextView>? = WeakReference(this)

    GlobalScope.launch(Dispatchers.Default) {
        val text = PrecomputedTextCompat.create(text, params)
        GlobalScope.launch(Dispatchers.Main) {
            ref?.get()?.let { textView ->
                TextViewCompat.setPrecomputedText(textView, text)
                lineCount.invoke(textView.lineCount)
            }
        }
    }
}
textView.getTextLineCount("Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............") { lineCount ->
     //count of lines is stored in lineCount variable
}

您是否在创建时执行此操作?视图尚未布局,因此
getLineCount()
暂时为0。如果稍后在窗口生命周期中执行此操作,将获得行计数。您将很难在创建时使用
onCreate
,但是
onWindowFocusChanged
使用
hasFocus=true
通常到目前为止已测量视图

textView.post()
建议也是一个很好的建议

基于此,如果您的目标是API 11或更高版本,则还有一种更干净的方法

如果
View.OnLayoutChangeListener

例如,在
ListAdapter.getView()中

if (h.mTitle.getLineCount() == 0 && h.mTitle.getText().length() != 0) {
    h.mTitle.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(final View v, final int left, final int top,
                final int right, final int bottom, final int oldLeft,
                final int oldTop, final int oldRight, final int oldBottom) {
            h.mTitle.removeOnLayoutChangeListener(this);
            final int count = h.mTitle.getLineCount();
            // do stuff
        }
    });
} else {
    final int count = h.mTitle.getLineCount();
    // do stuff
}

您还可以通过此函数计算行数:

private fun countLines(textView: TextView): Int {
        return Math.ceil(textView.paint.measureText(textView.text.toString()) /
                textView.measuredWidth.toDouble()).toInt()
    }
请记住,它在
回收视图上可能不会很好地工作

textview.getText().toString().split(System.getProperty("line.separator")).length

获取TextView的行数对我来说很好。

您也可以使用
预计算的TextCompat
获取行数。 常规方法:

fun getTextLineCount(textView: TextView, text: String, lineCount: (Int) -> (Unit)) {
    val params: PrecomputedTextCompat.Params = TextViewCompat.getTextMetricsParams(textView)
    val ref: WeakReference<TextView>? = WeakReference(textView)

    GlobalScope.launch(Dispatchers.Default) {
        val text = PrecomputedTextCompat.create(text, params)
        GlobalScope.launch(Dispatchers.Main) {
            ref?.get()?.let { textView ->
                TextViewCompat.setPrecomputedText(textView, text)
                lineCount.invoke(textView.lineCount)
            }
        }
    }
}
或者,您可以像这样为其创建扩展方法:

textview.setText(“Some text”);
textview.post(new Runnable() {
    @Override
    public void run() {
        int lineCount = textview.getLineCount();
        // Use lineCount here
    }
});
fun TextView.getTextLineCount(text: String, lineCount: (Int) -> (Unit)) {
    val params: PrecomputedTextCompat.Params = TextViewCompat.getTextMetricsParams(this)
    val ref: WeakReference<TextView>? = WeakReference(this)

    GlobalScope.launch(Dispatchers.Default) {
        val text = PrecomputedTextCompat.create(text, params)
        GlobalScope.launch(Dispatchers.Main) {
            ref?.get()?.let { textView ->
                TextViewCompat.setPrecomputedText(textView, text)
                lineCount.invoke(textView.lineCount)
            }
        }
    }
}
textView.getTextLineCount("Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............") { lineCount ->
     //count of lines is stored in lineCount variable
}

是的,我知道,我已经提到,在动态膨胀布局时,我不会遇到这个问题。我认为[这对您很有帮助][1][1]:@hardikjoshi:thax已经尝试过这个方法,但没有效果。See也可能重复。thax用于ans,但仍然存在问题这是正确的。您不能正确添加侦听器。将OnGlobalLayoutListener直接添加到TextView实例,而不是父布局。这仅在TextView包含换行符时有效,但对word wrapI不起作用。我猜这对您有效,因为您在代码中使用的是固定线宽,或预计算的线宽。但是,对于包装内容或匹配父项,这不起作用,因为这些值仍然需要计算。我在listview中采用了这种方法。我尝试了其他方法,但只有这个解决方案是完美的。我在recycleview中测试了它。not call listener any help感谢检查我的这个问题@VikashKumarVerma你能在recycleview中分享它是如何为你工作的吗?它对我不起作用。你能提供更多关于你的代码的详细信息吗?你试图获取
文本视图
的行数吗?也许适配器中的某些细节可能会导致视图的加载方式与预期不同?我用不同的方法解决了它,现在它工作正常了。我遵循了这个答案,感谢您分享为您的
RecycleView
工作的内容。这是一种有趣的方法,如果这段更简单的代码不适用于特定情况,那么绝对值得一试。:)不知道为什么这没有得到更多的选票。这需要选择所选答案。一旦布局完成,使用布局侦听器将提供回调,这意味着不可能在父级权限内进行任何需要的更改。示例:具有子对象的滚动视图。如何让所有的孩子根据其中一个孩子身上发生的事情进行调整。因此,OnPreDraw工作得更好,效率更高