Android 巧用二分法自动调整字体大小

开篇

  不BB, 直奔主题。

场景

  固定宽度的TextView,在不同尺寸的手机上显示效果不一样:小屏上会显示成两行,大屏上显示一行。
  在遇到这种情况时,我们该如何应对呢?我们的第一想法当然是自定义一个自动适配字体大小的TextView。

效果截屏

我们看到,在TextView宽度不变的情况下,它会根据文本长度自动调整字体大小。当然,在字符串不变的情况下,它同样会根据TextView宽度自动调整字体大小。

立即体验

扫描以下二维码下载体验App(从0.2.3版本开始,体验App内嵌版本更新检测功能):


JSCKit库传送门:https://github.com/JustinRoom/JSCKit

简析源码

AutoTextSizeView.java

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

在人生的道路上,当你的希望一个个落空的时候,你也要坚定,要沉着。 —— 朗费罗

你可能感兴趣的:(Android 巧用二分法自动调整字体大小)