今天突然想研究一下文字间距的问题,本来想着自己想个好的解决方案出来,结果发现自己真是太高估自己了……
满腔想法到了手上根本写不出来几行代码,最后还是老老实实去查了……
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方法里面,这我还怎么看(
下面两种方法思路都是很清晰的,可惜似乎没法很完美地解决问题……看情况使用吧。有谁有好办法欢迎说出来呀!
网友评论