39

I'm trying to dynamically resize my textview but getlinecount() method always returns me 0 even after settext() and invalidate(). I'm using the following code:

if (convertView == null) {
    convertView = lInflater.inflate(R.layout.listview, null);
    holder = new ViewHolder();
    holder.text2 = (TextView)convertView.findViewById(R.id.TextView02);
    convertView.setTag(holder);
} else {
    holder = (ViewHolder)convertView.getTag();
}

holder.text2.setText(arr2[position]);
holder.text2.invalidate();

int lineCnt = holder.text2.getLineCount();

holder is a static class as follows:

static class ViewHolder {
    TextView text2;
}

holder contains non null text2 and the content set is also non null.

halfer
  • 19,824
  • 17
  • 99
  • 186
neha
  • 6,327
  • 12
  • 46
  • 78

7 Answers7

92

I know this question is quite old, but in case anyone comes here looking for the actual answer:

holder.text2.setText(arr2[position]);
holder.text2.post(new Runnable() {
    @Override
    public void run() {
        int lineCnt = holder.text2.getLineCount();
        // Perform any actions you want based on the line count here.
    }
});
Scott
  • 2,593
  • 1
  • 22
  • 23
  • 4
    Also TextView needs to be VISIBLE or INVISIBLE for this method. When its GONE, it doesn't work. – Mustafa Berkay Mutlu May 12 '16 at 09:43
  • 1
    Is it right to use thread inside android b'coz i have been advised that it is the bad practice to use thread for this task – cammando Feb 06 '17 at 18:22
  • In my experience the post() method sometimes is still called *before* the layout pass. It happens randomly and not often, but in those cases we still get 0 value for line count even inside the mentioned Runnable. – Kurovsky Oct 20 '21 at 12:06
  • Yeah, this answer is about 9 years old and a lot has changed in that time. I no longer work on Android so I can't tell you the latest way to do this. – Scott Jan 28 '22 at 01:16
27

In order to fix this issue apply the following lines:

textView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        if (textView.getLineCount() > 1) {
            textView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    }
});

The OnGlobalLayoutListener will be called after every change to the TextView (after measuring it, drawing it, ..). Here you can catch changes to your TextView before it'll be drawn to the screen and do whatever you need with it.

The last line in the code is to remove the listener, it's important since we don't want to continue catching each layout change.

Nativ
  • 3,092
  • 6
  • 38
  • 69
24

It should be done on UI Thread by post method.

textView.post(new Runnable() {
    @Override
    public void run() {
        Log.v("Line count: ", textView.getLineCount()+"");
    }
});
Nima K
  • 995
  • 1
  • 8
  • 15
  • It works, but how does it work ? Bcoz, im setting the text to textview only in UI thread and without this textView.post, why doesn't it return me the linecount? – adi Dec 11 '19 at 09:09
10

getLineCount() will give you the correct number of lines only after a layout pass. That means the TextView must have been drawn at least once. I assume that at this point of time your Textview is not drawn hence you are getting 0 as the line count

DeRagan
  • 22,827
  • 6
  • 41
  • 50
  • Thanx Rahul.. That's right, but I'm afraid wheather where to set this height if even not after setText(). – neha Aug 20 '10 at 13:12
  • I am not sure where this code has been placed as of now. But if you have placed it under getview you text is shown/drawn only after you return the view object for that position – DeRagan Aug 20 '10 at 14:26
4

actually TextView.getLineCount() rely on TextView.mLayout.getLineCount but TextView.mLayout is lazy init before onMeasure, you can do like this:

       if (holder.contentTV.getLayout() == null) {
            holder.contentTV.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    holder.contentTV.getLineCount();
                    holder.contentTV.getViewTreeObserver().removeOnPreDrawListener(this);
                    return true;
                }
            });
        } else {
            holder.contentTV.getLineCount();
        }
cyrus
  • 81
  • 3
0

I create an extension for simple use:

/**
 * Function for detect when layout completely configure.
 */
fun View.afterLayoutConfiguration(func: () -> Unit) {
    viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
        override fun onGlobalLayout() {
            viewTreeObserver?.removeOnGlobalLayoutListener(this)
            func()
        }
    })
}

Example of use:

textView.afterLayoutConfiguration {
    val lineCount = textView.lineCount
    ...
}
SerjantArbuz
  • 982
  • 1
  • 12
  • 16
-1

I made an asynctask class and did my getLineCount()-related tasks in its onPostExecute method.

protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    new myAsyncTask().execute(null, null, null);
}

private class myAsyncTask extends AsyncTask<Void, Void, Void> {

    @Override
    protected Void doInBackground(final Void... params) {
        // It's okay to leave this as it is
        return null;
    }

    @Override
    protected void onPostExecute(final Void result) {
        super.onPostExecute(result);
        //DO YOUR TASK HERE, getLineCount(), etc.       
    }
}
  • 7
    If any down-votes, probably because using a timer to wait for layout to be complete is risky, since there is no guarantee that layout will be complete when your async task runs. Watching for layout/drawing related events would be better (I don't have any code yet or I would post it). – eselk Jan 04 '13 at 00:10
  • Risky? It's not risky, it's just wrong! – Marian Paździoch May 16 '23 at 09:19