开篇
不BB, 直奔主题。
场景
固定宽度的TextView,在不同尺寸的手机上显示效果不一样:小屏上会显示成两行,大屏上显示一行。
在遇到这种情况时,我们该如何应对呢?我们的第一想法当然是自定义一个自动适配字体大小的TextView。
效果截屏
我们看到,在TextView宽度不变的情况下,它会根据文本长度自动调整字体大小。当然,在字符串不变的情况下,它同样会根据TextView宽度自动调整字体大小。
立即体验
扫描以下二维码下载体验App(从0.2.3
版本开始,体验App内嵌版本更新检测功能):
JSCKit库传送门:https://github.com/JustinRoom/JSCKit
简析源码
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次匹配,匹配效率是真的很高。😊
童鞋们,如果你们觉得不错的话给我点个💗吧,谢谢!!!
篇尾
Wechat:eoy9527
。
在人生的道路上,当你的希望一个个落空的时候,你也要坚定,要沉着。 —— 朗费罗