Global Layout Listener to the Rescue – How to fix TextView cutoff

I had a layout that had one TextView that had variable size. I figured no problem it will just display what it can and be done. Nope. I was wrong it was doing this nasty line cut off.




My initial thought was I’ll just set the max lines for the TextView(thus preventing a half line from displaying) and be done with it. Worked for a little bit until I noticed that this didn’t work for all of the rows in the RecyclerView. The other views in the viewgroup had data that were higher priority and needed to be displayed. So the whatever space was left would go to the poor view. Thus causing the cut off problem.

A quick search showed that I wasn’t the only one with this problem.

Possible Options

layout_gravity

The first suggestion was to use layout_gravity=”fill”. This didn’t work for me because the containing ViewGroup was Relative not Linear.

Custom TextView

Another suggestion was to create a custom TextView. In the event that you have lots of TextViews that have this problem in your code base it could be a good solution. In that case you can do something like the following:

public class FullLineTextView extends TextView {

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

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

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        // calculate the max lines we can show
        int maxLines = h / this.getLineHeight();
        if (maxLines > 0) {
            setLines(maxLines);
        }
    }

}

For me this was overkill as I only had one TextView in my code base that had this particular problem.

Scrollable

I had an excellent suggestion from Romain Guy about making the TextView scrollable. As Romain pointed out it doesn’t play well with adapter views. Unfortunately in my case the TextView was in a RecyclerView, so this wouldn’t work in my case.

Set Lines

I figured oh I could just do the following:

TextView dynamicSizeTextView = (TextView)findViewById(R.id.annoyingTextView);
float size = dynamicSizeTextView.getLineHeight();
int height = dynamicSizeTextView.getHeight();
int maxLines = (int)(height / size);
dynamicSizeTextView.setLines(maxLines);

But it didn’t work :/

The problem with this is that I was doing this before the layout pass had happened. As a result the getLineHeight and getHeight were 0. Bummer!

Solution – OnGlobalLayoutListener to the Rescue

I remembered about OnGlobalLayoutListener. From the docs:

Interface definition for a callback to be invoked when the global layout state or the visibility of views within the view tree changes.

This means that we will get a callback after the layout pass. We can then use the above code in the callback to calculate the desired size and set it:

    final TextView dSTextView = (TextView)findViewById(R.id.annoyingTextView);
    dSTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
    
        @Override
        public void onGlobalLayout() {
            dSTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            float lineHeight = dSTextView.getLineHeight();
            int maxLines = (int) (dSTextView.getHeight() / lineHeight);

            if (dSTextView.getLineCount() != maxLines) {
                dSTextView.setLines(maxLines);
            }

        }
    });

I’d like to point out that this code only get’s called after the first layout change. In my particular case it’s setup in an RecyclerView Adapter, so it get’s setup once on every bind call. Without this line:

dSTextView.getViewTreeObserver().removeOnGlobalLayoutListener(this);

We’d be adding a new listener on every bind call. If we don’t remove them we’d end up with tons of callbacks.

That’d be bad.

Slow Annoying Builds?

Sign up for FREE Gradle Tips on faster android builds, and how to make android development easier.

Jim

Android Development

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>