I am trying to use Autosizing TextViews in a RecyclerView, but when I scroll a few times the text gets so small that it\'s obviously not working properly.
Example of my
Pavel Haluza's answer's approach was great. However, it didn't work, probably because he missed a line setTextSize(TypedValue.COMPLEX_UNIT_PX, maxTextSize);
.
Here is my updated version:
public class MyTextView extends AppCompatTextView {
private int minTextSize;
private int maxTextSize;
private int granularity;
public MyTextView(Context context) {
super(context);
init();
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
minTextSize = TextViewCompat.getAutoSizeMinTextSize(this);
maxTextSize = TextViewCompat.getAutoSizeMaxTextSize(this);
granularity = Math.max(1, TextViewCompat.getAutoSizeStepGranularity(this));
}
@Override
public void setText(CharSequence text, BufferType type) {
// this method is called on every setText
disableAutoSizing();
setTextSize(TypedValue.COMPLEX_UNIT_PX, maxTextSize);
super.setText(text, type);
post(this::enableAutoSizing); // enable after the view is laid out and measured at max text size
}
private void disableAutoSizing() {
TextViewCompat.setAutoSizeTextTypeWithDefaults(this, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
}
private void enableAutoSizing() {
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this,
minTextSize, maxTextSize, granularity, TypedValue.COMPLEX_UNIT_PX);
}}
Just .setText("") before resetting the text size you want. That ensures that you are not setting the textsize and then immediately autoresizing using the previous text value in the TextView. Like this:
TextView wordWordTextView = getView().findViewById(R.id.wordWordTextView);
wordWordTextView.setAlpha(0.0f);
wordWordTextView.setText("");
wordWordTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 50);
wordWordTextView.setText(wordStr);
wordWordTextView.animate().alpha(1.0f).setDuration(250);
The issue I've seen with this is that setting your view height to be wrap_content
allows the text size to get smaller, but the text will never get bigger again. This is why the documentation recommends to not use wrap_content
for the view size. However, I've found that if you turn off the auto-resizing, set the text size to whatever the max is, then re-enable auto-resizing, the text size resets to the largest size and scales down as necessary.
So my view in XML would look like:
<android.support.v7.widget.AppCompatTextView
android:id="@+id/text_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:textAllCaps="true"
android:textColor="@android:color/white"
android:textSize="42sp"
app:autoSizeMinTextSize="26dp"
app:autoSizeMaxTextSize="42dp"
app:autoSizeTextType="none"/>
Then in my ViewHolder
when I bind my text to the view:
TextView title = view.findViewById(R.id.text_title);
String titleValue = "Some Title Value";
// Turn off auto-sizing text.
TextViewCompat.setAutoSizeTextTypeWithDefaults(title,
TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
// Bump text size back up to the max value.
title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 42);
// Set your text as normal.
title.setText(titleValue);
// Post a runnable to re-enable auto-sizing text so that it occurs
// after the view is laid out and measured at max text size.
title.post(new Runnable() {
@Override
public void run() {
TextViewCompat
.setAutoSizeTextTypeUniformWithConfiguration(title,
26, 42, 1, TypedValue.COMPLEX_UNIT_DIP);
}
});
I packaged Michael Celey's answer into a class. The parameters app:autoSizeMinTextSize
, app:autoSizeMaxTextSize
, app:autoSizeTextType
are taken from xml.
public class AutosizingTextView extends AppCompatTextView {
private int minTextSize;
private int maxTextSize;
private int granularity;
public AutosizingTextView(Context context) {
super(context);
init();
}
public AutosizingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public AutosizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
minTextSize = TextViewCompat.getAutoSizeMinTextSize(this);
maxTextSize = TextViewCompat.getAutoSizeMaxTextSize(this);
granularity = TextViewCompat.getAutoSizeStepGranularity(this);
}
@Override
public void setText(CharSequence text, BufferType type) {
// this method is called on every setText
disableAutosizing();
super.setText(text, type);
post(this::enableAutosizing); // enable after the view is laid out and measured at max text size
}
private void disableAutosizing() {
TextViewCompat.setAutoSizeTextTypeWithDefaults(this, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
}
private void enableAutosizing() {
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this,
minTextSize, maxTextSize, granularity, TypedValue.COMPLEX_UNIT_PX);
}
}```
Autosizing TextViews
Android 8.0 (API level 26) allows you to instruct a TextView to let the text size expand or contract automatically to fill its layout based on the TextView's characteristics and boundaries.
Note: If you set autosizing in an XML file, it is not recommended to use the value "wrap_content" for the layout_width or layout_height attributes of a TextView. It may produce unexpected results.
You should bound height
android:layout_height="30dp"
the above solutions didn't work for me so here's mine
public class MyTextView extends AppCompatTextView {
...
@Override
public final void setText(CharSequence text, BufferType type) {
// work around stupid auto size text not *growing* the font size we re binding in a RecyclerView if previous bind caused a small font
int minTextSize = 0, maxTextSize = 0, granularity = 0;
boolean doHack = TextViewCompat.getAutoSizeTextType(this) != TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE;
if (doHack) {
minTextSize = TextViewCompat.getAutoSizeMinTextSize(this);
maxTextSize = TextViewCompat.getAutoSizeMaxTextSize(this);
if (minTextSize <= 0 || maxTextSize <= minTextSize) { // better than validateAndSetAutoSizeTextTypeUniformConfiguration crashing
if (BuildConfig.DEBUG)
throw new AssertionError("fix ya layout");
doHack = false;
} else {
granularity = TextViewCompat.getAutoSizeStepGranularity(this);
if (granularity < 0)
granularity = 1; // need this else setAutoSizeTextTypeUniformWithConfiguration barfs. TextView.UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = 1.
// make the TextView have 0 size so setAutoSizeTextTypeUniformWithConfiguration won't do calculations until after a layout pass using maxSize
TextViewCompat.setAutoSizeTextTypeWithDefaults(this, TextViewCompat.AUTO_SIZE_TEXT_TYPE_NONE);
setTextSize(TypedValue.COMPLEX_UNIT_PX, maxTextSize);
measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY));
setRight(getLeft());
setBottom(getTop());
requestLayout();
}
}
super.setText(text, type);
if (doHack)
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(this, minTextSize, maxTextSize, granularity, TypedValue.COMPLEX_UNIT_PX);
}
...
}