开篇
不BB, 直奔主题。
场景
固定宽度的TextView,在不同尺寸的手机上显示效果不一样:小屏上会显示成两行,大屏上显示一行。
在遇到这种情况时,我们该如何应对呢?我们的第一想法当然是自定义一个自动适配字体大小的TextView。
效果截屏
我们看到,在TextView宽度不变的情况下,它会根据文本长度自动调整字体大小。当然,在字符串不变的情况下,它同样会根据TextView宽度自动调整字体大小。
简析源码
public class AutoTextSizeView extends AppCompatTextView implements IViewAttrDelegate{ public AutoTextSizeView(Context context) { super(context); initAttr(context, null, 0); } public AutoTextSizeView(Context context, AttributeSet attrs) { super(context, attrs); initAttr(context, attrs, 0); } public AutoTextSizeView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttr(context, attrs, defStyleAttr); } @Override public void initAttr(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { setGravity(Gravity.CENTER); setMaxLines(1); }
1、我们在监听到View宽度发生变化时,自动调整字体大小:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //do nothing if it's width was not changed if (oldw == w){ return; } resetTextSizeIfNecessary(); }
2、因为自动适配字体大小的过程是一个不断循环去匹配的一个过程,所以我们开启一个线程去干这件事。
private void resetTextSizeIfNecessary() { removeCallbacks(r); postDelayed(r, 20); } private Runnable r = new Runnable() { @Override public void run() { loop(); } };
3、匹配字体大小的核心算法:当前字体大小
float textSize = getPaint().getTextSize();
A、字符宽度 >
TextView
的宽度,说明textSize
大了,调整字体大小:textSize = (textSize + textSize / 2) / 2;
B、字符宽度 <TextView
的宽度 - 一个字符宽度,说明textSize
小了,调整字体大小:textSize = (textSize + textSize * 2) / 2;
通过递归反复匹配字体大小,直到匹配到相对合适的字体大小为止。这个过程往往经过1次或者几次就能匹配到,利用二分法的思想匹配效率很高。
private void loop() { CharSequence text = getText(); if (text == null || text.length() == 0) return; int maxTextWidth = getWidth() - getPaddingLeft() - getPaddingRight(); float textWidth = getPaint().measureText(text, 0, text.length()); float textSize = getPaint().getTextSize(); float alphaWidth = getPaint().measureText("a"); if (textWidth >= (maxTextWidth - alphaWidth * 2) && textWidth <= maxTextWidth) { //匹配到合适的字体大小了 //这里我试图直接调用invalidate()刷新视图,但是并没有达到想要的结果 //我们必须重新setText(...) //reset text //call it's onMeasure(int, int) and onDraw(Canvas) //增加这个方法,只是为了区分是自动适配字体大小后setText(...)还是我们手动调用setText(...) setAutoSizeText(text, false); return; } //字体大了 //here is the idea of binary search if (textWidth > maxTextWidth) { textSize = textSize + textSize / 2; getPaint().setTextSize(textSize / 2); } //字体小了 if (textWidth < maxTextWidth - alphaWidth * 2) { textSize = textSize + textSize * 2; getPaint().setTextSize(textSize / 2); } loop(); } public void setAutoSizeText(CharSequence text, boolean resizeImmediately){ setText(text); if (resizeImmediately) resetTextSizeIfNecessary(); }
从26.0匹配到87.75,之经历过了4次匹配,匹配效率是真的很高。
童鞋们,如果你们觉得不错的话给我点个吧,谢谢!!!
作者:JustinRoom
链接:https://www.jianshu.com/p/ec3cf23044b6