1

I'm trying to make a textview that scales the contents so that it all shows, no cropping, multi lines or elipsis.

I'll quickly run through the layout just to put it into context.

*There's a gridview with several rows and columns. *The items for the gridview are provided by an adapter which returns (pseudo markup :) ):-

<LinearLayout Orientation=vertical>
    <MyCustomTextview name='titletext' Not multiline />
    <MyCustomTextview name='itemtext' Not multiline />
</LinearLayout>

I've tried overriding onSizeChanged as this is where several google results pointed me and I started with:-

...
if (this.getLinecount() > 1)
{
    this.setTextSize(... currentsize -10);
}
...

and this gave me really small text where it would have appeared on more than one line, so I knew I could resize and find out if the TextView had more than one line correctly.

so I progressed to:-

while (this.getLinecount() > 1)
{
    this.setTextSize(... currentsize - 1);
}

which I expected to run until the text was small enough, but after the call to setTextSize getLineCount() returns 0, I believe this is because it needs to recalculate the text size so I tried various combinations of refreshLayout(), forceLayout() and invalidate() to no avail.

I've an idea for perhaps a better approach, but I don't know if it's easily attainable:-

onSizeChanged is called after it is put into the Gridview by the Adapter so I will execute my code there, but is the following possible and how:-

  • The TextView is set to a width of fill parent, can I accurately tell a pixel size of the width of available space for text? (I believe there may be padding and such applied)
  • I then intend to use measureText(String) to see if the text will fit, if not decrease size and check again.

I believe that would be a good solution, but I'm not sure on the accurate measurement of available space, and if the event I've chosen would be best.

Any assistance would be appreciated.

  • Anthony
Anthony Graham
  • 451
  • 4
  • 13
  • check this - http://stackoverflow.com/questions/7259016/scale-text-in-a-view-to-fit/7259136#7259136 – Ron Mar 19 '14 at 12:16

2 Answers2

1

You do not need to create Custom Text View for this. Use ViewTreeObserver to get the required functionality.

TextView tv = (TextView)findViewById(R.id.mytextview); 
ViewTreeObserver vto = tv.getViewTreeObserver();
vto.addOnGlobalLayoutListener(this);

@Override
public void onGlobalLayout() {
    if (1 < tv.getLineCount()) {
        tv.setTextSize(TypedValue.COMPLEX_UNIT_PX,
                tv.getTextSize() - 2);
    }
}
Ion Aalbers
  • 7,830
  • 3
  • 37
  • 50
PravinCG
  • 7,688
  • 3
  • 30
  • 55
  • Will this code also "loop", i.e. onGlobalLayout gets called over and over until the test fits? – CjS Feb 20 '12 at 11:06
  • Yes it will, the reason is that unless the views are drawn you will never get the actual size. This code works dynamically in that it reduces the size iteratively till it fits a single line. – PravinCG Feb 20 '12 at 11:21
  • Does this code result in the text being painted on screen several times before the correct size is reached? – Anthony Graham Feb 20 '12 at 12:03
  • Theoretically it does but would be very difficult to discern for the Human Eye. It gets done very quickly for you to notice. – PravinCG Feb 20 '12 at 12:11
  • 1
    I just tried this, and although it works, it is definitely noticeable to the user. I tried on a Galaxy Nexus running 4.2.2, without logcat hooked up. – Sky Kelsey May 30 '13 at 04:01
  • It is noticeable to the user for a fleeting second, I agree. – PravinCG Jun 03 '13 at 07:49
0

Why don't you try the same sort of approach, but use measureText instead?

while (mPaint.measureText(MyTextString)>mTextView.getWidth() ) //maybe adjust for padding/margins etc
{
    mPaint.setTextSize(... currentsize - 1);
}
CjS
  • 2,037
  • 2
  • 21
  • 29
  • Sorry -- just saw you've mentioned this already. But that would be my approach. – CjS Feb 20 '12 at 10:54
  • Hi, this is the preferred aproach for me, the specific issue I have with this method is I'm unsure how to adjust for margins and padding (My understanding is getWidth() returns the external size of the TextView not the internal usable space) I see your code is commented with this in mind, do you happen to know what would be the best / most accurate way to determine usable space within a textbox? (In hindsight, I should have titled my question as such). – Anthony Graham Feb 20 '12 at 11:18
  • I'm not sure, actually :) But I also like @PravinCG's answer. However, I'm thinking a hybrid approach might be best. You use the ViewTreeObserver to trigger the resizing if ever the text changes, but you use MeasureText to calculate the right fontsize once and for all. BTW You obviously also have to check for the case where even at font-size 1, it still doesn't fit on one line and make some other plan there – CjS Feb 20 '12 at 11:34
  • int availableWidth = (int) (textWidth - tv.getPaddingLeft() - tv.getPaddingRight()); seems to be the best way to measure the available space:- My understanding is that onSizeChanged should fire if the area available to the TextView changes, for example an orientation change. So if I override onSizeChanged, and within that get the painter, use that to measure text, decrease size, repeat if required, then once the right size is found set the textview's size to that, I think that would do it, though I'm sure I'm going to find it won't work when I try :) – Anthony Graham Feb 20 '12 at 12:08
  • Sorry, int availableWidth = (int) (tv.getWidth() - tv.getPaddingLeft() - tv.getPaddingRight()); – Anthony Graham Feb 20 '12 at 12:14
  • I'm accepting this as you validated my initial thoughts, and I went on to get working code. thanks :) – Anthony Graham Mar 01 '12 at 12:13