美文网首页
Android TextView文字间距

Android TextView文字间距

作者: 景山道人 | 来源:发表于2019-07-20 22:59 被阅读0次

今天突然想研究一下文字间距的问题,本来想着自己想个好的解决方案出来,结果发现自己真是太高估自己了……
满腔想法到了手上根本写不出来几行代码,最后还是老老实实去查了……
Reference

对于这件事情,除了21版本以上有官方提供的方法作为解决方案以外,21版本以下似乎找不到什么统一的比较好的办法,(不要提setTextScaleX),只能想办法另辟蹊径。
查询一番总算是有所收获,顺便了解了一波SpannableString的用法。
总结了一个工具类如下:
编译器版本27

import android.os.Build;
import android.support.annotation.RequiresApi;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.ScaleXSpan;
import android.widget.TextView;

public class LetterSpaceUtils {

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public static void setLetterSpaceAbove21(TextView target, float letterSpacing) {
        if (null != target) {
            target.setLetterSpacing(letterSpacing);
        }
    }

    public static void setLetterSpaceBelow21(TextView target, float letterSpacing) {
        if (null == target || TextUtils.isEmpty(target.getText()) || 0 == letterSpacing) {
            return;
        }
        String text = target.getText().toString();
        char[] chars = text.toCharArray();
        // 眼熟吗hhh, TextUtils.join(), 因为char[]不满足参数条件,所以我直接自己写了
        StringBuilder sb = new StringBuilder();
        boolean firstTime = true;
        for (char ch : chars) {
            if (firstTime) {
                firstTime = false;
            } else {
                sb.append(" ");
            }
            sb.append(ch);
        }
        SpannableString resultString = new SpannableString(sb.toString());
        if (sb.toString().length() > 1) {
            for (int i = 1; i < sb.toString().length(); i += 2) {
                // 空格加在字符后面,所以从1开始,每次跳两个,把每个空格宽设置为参数倍数
                resultString.setSpan(new ScaleXSpan(letterSpacing), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
        target.setText(resultString);
    }

    public static void setSingleLineLetterSpace(TextView target, int offset) {
        SpannableString resultString = new SpannableString(target.getText());
        resultString.setSpan(new SpacingSpan(offset), 0, target.getText().length() - 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        target.setText(resultString);
    }
}

方法一:官方的轮子,满足条件的情况下除非我脑子有问题才会不用这个方法……
方法二:给字符串中的每个字符添加空格,然后把设置空格的宽度来作为间距,缺点是官方原来的换行逻辑会失效,中文还好,英文的话,单词会中断在很奇怪的地方(因为全是空格),而且如果你要复制文本的话,会把空格一起复制走
方法三:在每个字符绘制的时候添加一个offset作为间距,感觉比起方法二来说更像一个正经点的解决方案,但问题是这样设置完了好像没法换行……(是因为重写了draw方法导致的吗?)二里面那些问题很可能也是存在的,用到的类在下面

import android.graphics.Canvas;
import android.graphics.Paint;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.style.ReplacementSpan;

public class SpacingSpan extends ReplacementSpan {

    private float offsetX; // 指每个字增加多少间距

    public SpacingSpan(float offsetX) {
        this.offsetX = offsetX;
    }

    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
        // 这里我看大神们写的offset * 数值有好几种值,
        // 总之就是有多少个字就要加上多少倍的offset,我认为是end - start
        return (int) (paint.measureText(text, start, end) + offsetX * (end - start));
    }

    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
        float leftX = x; // 绘制左起x坐标
        for (int i = start; i < end; i++) {
            canvas.drawText(text, i, i + 1, leftX, y, paint);
            leftX += paint.measureText(text, i, i + 1) + offsetX;
            // 绘制完要加上多绘制的部分,才能让下次绘制从正确的地方开始
        }
    }
}

官方的那个方法我原本想看看,然后看着看着发现走进了一个native方法里面,这我还怎么看(
下面两种方法思路都是很清晰的,可惜似乎没法很完美地解决问题……看情况使用吧。有谁有好办法欢迎说出来呀!

相关文章

网友评论

      本文标题:Android TextView文字间距

      本文链接:https://www.haomeiwen.com/subject/gyxrqctx.html