如何获取TextView的行数?

时间:2012-08-20 12:09:22

标签: 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 OnGlobalLayoutListener() {

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

    }
});

它返回确切的输出。

但这仅适用于静态布局。

当我动态地给布局充气时,这不再起作用了。

如何在TextView中找到行数?

10 个答案:

答案 0 :(得分:70)

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

textview.setText(“Some text”);
textview.post(new Runnable() {
    @Override
    public void run() {
        int lineCount = textview.getLineCount();
        // Use lineCount here
    }
});

答案 1 :(得分:33)

如此post中所述,

  

getLineCount()只会在您之后提供正确的行数   布局通行证。

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

答案 2 :(得分:21)

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

答案 3 :(得分:8)

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

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

留下了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 );

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

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

享受!

答案 4 :(得分:8)

基于@secnelis idea,如果你的目标是API 11或更高版本,甚至会有更简洁的方法。

如果TextView

,您可以使用已有的内置功能,而不是扩展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
}

答案 5 :(得分:8)

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

答案 6 :(得分:1)

您还可以使用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
}

答案 7 :(得分:0)

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

textView.post()建议也很好

答案 8 :(得分:0)

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

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

请记住,尽管它在RecyclerView上可能无法很好地工作。

答案 9 :(得分:0)

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

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