69

我想获取文本视图的行数

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

    }
});

它返回准确的输出。

但这仅适用于静态布局。

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

如何找到 TextView 中的行数?

4

10 回答 10

107

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

textview.setText(“Some text”);
textview.post(new Runnable() {
    @Override
    public void run() {
        int lineCount = textview.getLineCount();
        // Use lineCount here
    }
});
于 2015-09-01T20:49:42.117 回答
40

正如这篇文章中提到的,

getLineCount()只有在布局通过后才会为您提供正确的行数。

这意味着您需要TextView在调用该getLineCount()方法之前渲染第一个。

于 2012-08-20T12:16:07.350 回答
32

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
        }
});
于 2014-03-06T07:26:09.297 回答
22
 textView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

        @Override
        public boolean onPreDraw() {
            // Remove listener because we don't want this called before _every_ frame
            textView.getViewTreeObserver().removeOnPreDrawListener(this)

            // Drawing happens after layout so we can assume getLineCount() returns the correct value
            if(textView.getLineCount() > 2) {
                // Do whatever you want in case text view has more than 2 lines
            }  

            return true; // true because we don't want to skip this frame
        }
});
于 2017-07-22T07:36:47.753 回答
10

我认为这个问题的症结在于人们希望能够提前找出 TextView 的大小,以便他们可以动态调整它的大小以很好地适应文本。一个典型的用途可能是创建对话气泡(至少那是我正在做的)。

我尝试了几种解决方案,包括使用这里讨论的 getTextBounds() 和 measureText() 。不幸的是,这两种方法都有些不精确,并且无法解释换行符和未使用的行空间。所以,我放弃了这种方法。

剩下的就是 getLineCount(),它的问题是你必须在 getLineCount() 之前“渲染”文本会给你行数,这使它成为先有鸡还是先有蛋的情况。我阅读了涉及侦听器和布局的各种解决方案,但简直不敢相信没有更简单的东西。

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

无论如何,这是关键代码(假设您已经知道谈话气泡的 lineWidth 和 lineHeight 基于字体的单行):

TextView talkBubble;
// No peeking while we set the bubble up.
talkBubble.setVisibility( View.INVISIBLE );
// I use FrameLayouts so my talk bubbles can overlap
// lineHeight is just a filler at this point
talkBubble.setLayoutParams( new FrameLayout.LayoutParams( lineWidth, lineHeight ) );
// setText() calls invalidate(), which makes getLineCount() do the right thing.
talkBubble.setText( "This is the string we want to dynamically deal with." );
int lineCount = getLineCount();
// Now we set the real size of the talkBubble.
talkBubble.setLayoutParams( new FrameLayout.LayoutParams( lineWidth, lineCount * lineHeight ) );
talkBubble.setVisibility( View.VISIBLE );

无论如何,就是这样。下一次重绘将为您的文本提供一个量身定制的气泡。

注意:在实际程序中,我使用单独的气泡来确定文本行,以便我可以根据长度和宽度动态调整真实气泡的大小。这使我可以从左到右缩小气泡以进行简短的陈述等。

享受!

于 2013-07-12T14:43:19.457 回答
8

基于@secnelis 的想法,如果您以 API 11 或更高版本为目标,还有更简洁的方法。

TextView如果你可以使用已经内置的功能,而不是扩展 aView.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
}
于 2016-03-14T13:33:12.237 回答
6

您还可以PrecomputedTextCompat用于获取行数。常规方法:

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

调用这个方法:

getTextLineCount(textView, "Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............") { lineCount ->
     //count of lines is stored in lineCount variable
}

或者,也许您可​​以像这样为其创建扩展方法:

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
}
于 2019-11-21T09:15:42.767 回答
4

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

private fun countLines(textView: TextView): Int {
        return Math.ceil(textView.paint.measureText(textView.text.toString()) /
                textView.measuredWidth.toDouble()).toInt()
    }

请记住,它可能无法很好地工作RecyclerView

于 2019-05-30T17:16:49.097 回答
1
textview.getText().toString().split(System.getProperty("line.separator")).length

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

于 2019-11-15T18:38:40.000 回答
0

你在做这个onCreate吗?视图尚未布局,因此暂时getLineCount()为 0。如果稍后在 Window LifeCycle 中执行此操作,您将获得行数。你会很难做到这一点onCreate,但通常现在已经测量onWindowFocusChangedhasFocus=true视图。

建议textView.post()也不错

于 2016-02-26T18:19:45.387 回答