grantland / android-autofittextview

A TextView that automatically resizes text to fit perfectly within its bounds.
Apache License 2.0
4.27k stars 688 forks source link

Compute font size in onMeasure() to support height, line spacing, drawables, etc. #8

Closed drbild closed 9 years ago

drbild commented 10 years ago

The previous version used a separate StaticLayout to determine if the text was too large for the desired TextView height. However, accurately mimicing the TextView appearance (and thus size) is nearly impossible when one considers full information, e.g., line spacing, left/right/top/bottom drawables, replacement spans, etc. Before API 16, information like line spacing was simply unavailable to subclasses.

This commit uses a different approach. The onMeasure() method is overriden to tell the superclass TextView to measure at its desired height. If this is too tall or short (in pixels or lines), the font is decreased or increased and the view measured again. The same binary search process as before is used.

This commit does not work when the 'android:singleLine' attribute is set. The text is clipped. Instead, 'android:maxlines="1"' should be set and the desired ellipsizing, horizontal scrolling, and single-line transforms set manually.

The TextView treats 'singleLine' as a special case (i.e., it's handled differently than 'android:maxLines="1"'), but does not expose this state. Ideally, we would disable 'singleLine' in onMeasure(), before calling super.onMeasure(), and reenable it after setting the fitted font size. However, as there is no (supported) way to determine if the 'singleLine' property was set, this is not possible. Simply disabling maxLines and ellipsizing does not work---I belive because the TextView uses the cached internal StaticLayout.

Ultimately, using 'singleLine' in an autosizing text view is weird anyway, since 'singleLine' implies 'ellipsized'. (Ellipizing after the font size has reached the minimum is still supported).

drbild commented 10 years ago

@grantland , any thoughts on this approach?

(Not supporting 'singleLine' is clearly non-ideal, but I don't want to spend time trying to hack-in support for it if you're not interested in this onMeasure() approach anyway. I don't need 'singleLine', so won't be adding it for just for my own personal fork.)

johnkil commented 9 years ago

Must be merged :+1:

grantland commented 9 years ago

Originally I left this since I was working on decoupling autofitting and TextView and this solution relied on the coupled implementation. Once I accomplished that with AutofitHelper, I meant to comment here but personal life took over.

It'd be great to see this re-invented with AutofitHelper in mind, but it'd probably have to be in the form of a completely different PR.

I'll leave this open for now, but if I don't hear a response I'll end up closing it and try looking at solving this myself if I have time.

drbild commented 9 years ago

Are you interested in the approach taken in this PR, i.e., a binary search within onMeasure() rather than over a separate static layout attempting to mimic the actual TextView? The onMeasure() approach doesn't really fit the AutofitHelper approach, as far as I can see. If not, I'll close this PR.

grantland commented 9 years ago

Unfortunately, no since this solution isn't portable to other types of TextViews like AutofitHelper. Thanks for your contribution!

drbild commented 9 years ago

Ok, no problem!