How to get number of lines of TextView?

前端 未结 10 2460
萌比男神i
萌比男神i 2020-11-28 09:29

I want to get the number of lines of a text view

textView.setText(\"Test line 1 Test line 2 Test line 3 Test line 4 Test line 5.............\")
相关标签:
10条回答
  • 2020-11-28 10:01

    I think the crux of this question is that people want to be able to find out the size of a TextView in advance so that they can dynamically resize it to nicely fit the text. A typical use might be to create talk bubbles (at least that was what I was working on).

    I tried several solutions, including use of getTextBounds() and measureText() as discussed here. Unfortunately, both methods are slightly inexact and have no way to account for line breaks and unused linespace. So, I gave up on that approach.

    That leaves getLineCount(), whose problem is that you have to "render" the text before getLineCount() will give you the number of lines, which makes it a chicken-and-egg situation. I read various solutions involving listeners and layouts, but just couldn't believe that there wasn't something simpler.

    After fiddling for two days, I finally found what I was looking for (at least it works for me). It all comes down to what it means to "render" the text. It doesn't mean that the text has to appear onscreen, only that it has to be prepared for display internally. This happens whenever a call is made directly to invalidate() or indirectly as when you do a setText() on your TextView, which calls invalidate() for you since the view has changed appearance.

    Anyway, here's the key code (assume you already know the talk bubble's lineWidth and lineHeight of a single line based on the font):

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

    Anyway, that's it. The next redraw will give a bubble tailor-made for your text.

    Note: In the actual program, I use a separate bubble for determining lines of text so that I can resize my real bubble dynamically both in terms of length and width. This allows me to shrink my bubbles left-to-right for short statements, etc.

    Enjoy!

    0 讨论(0)
  • 2020-11-28 10:01

    Based on @secnelis idea, there is even a more clean way if you target API 11 or higher.

    Instead of extending a TextView you can use already built-in functionality if View.OnLayoutChangeListener

    In ListAdapter.getView(), for instance

    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
    }
    
    0 讨论(0)
  • 2020-11-28 10:13

    You could also use PrecomputedTextCompat for getting the number of lines. Regular method:

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

    Call this method:

    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
    }
    

    Or maybe you can create extension method for it like this:

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

    and then you call it like this:

    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
    }
    
    0 讨论(0)
  • 2020-11-28 10:17

    You can also calculate the amount of lines through this function:

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

    Keep in mind that It may not work very well on a RecyclerView though.

    0 讨论(0)
  • 2020-11-28 10:17
    textview.getText().toString().split(System.getProperty("line.separator")).length
    

    It works fine for me to get number of lines of TextView.

    0 讨论(0)
  • 2020-11-28 10:19

    I was able to get getLineCount() to not return 0 using a post, like this:

    textview.setText(“Some text”);
    textview.post(new Runnable() {
        @Override
        public void run() {
            int lineCount = textview.getLineCount();
            // Use lineCount here
        }
    });
    
    0 讨论(0)
提交回复
热议问题