字符样式/跨度设置字母间距

7

@CommonsWare 感谢您的评论,顺便说一下,我已经修复了它 :) - alexbirkett
3个回答

13

所以我通过编写自己的span class解决了这个问题,它适用于21级及以上的API。

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Parcel;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;

/**
 * Created by alex on 19/02/2015.
 */
@TargetApi(21)
public class LetterSpacingSpan extends MetricAffectingSpan {
    private float letterSpacing;

    /**
     * @param letterSpacing
     */
    public LetterSpacingSpan(float letterSpacing) {
        this.letterSpacing = letterSpacing;
    }

    public float getLetterSpacing() {
        return letterSpacing;
    }

    @Override
    public void updateDrawState(TextPaint ds) {
        apply(ds);
    }

    @Override
    public void updateMeasureState(TextPaint paint) {
        apply(paint);
    }

    private void apply(TextPaint paint) {
        paint.setLetterSpacing(letterSpacing);
    }

}

这解决了什么问题?如果是API级别21及以上,你可以直接在TextView上设置字间距。 - qtyq
4
这让你可以在文本的一部分(一个 span)上设置字符间距。如果你在整个 TextView 上设置字符间距,它将应用于整个 TextView。 - alexbirkett

7
你可以查看这个自定义类的实现,它实现了你所需的功能。
编辑:好的,如果你想仅对一小部分文本应用此功能,可以按照以下步骤操作:
CharSequence firstPart = "First Part";
CharSequence thirdPart = "Third Part";
SpannableStringBuilder middlePart = new SpannableStringBuilder("Middle Part");

int spacing = 2;
final String nonBreakingSpace = "\u00A0";


for (int i = middlePart.length() - 1; i >= 1; i--){
    middlePart.insert(i, nonBreakingSpace);
    middlePart.setSpan(new ScaleXSpan(spacing), i, i + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

((TextView) rootView.findViewById(R.id.text_view)).setText(TextUtils.concat(firstPart, " ", middlePart, " ", thirdPart));

虽然不完美。

编辑:

明确一下,这是上面代码的结果。

第一行(“自定义Spannable”)是上面代码的结果。第二行(“字母间距”)是属性android:letterSpacing="1"的结果。

enter image description here

正如你所看到的和我所说的,它并不完美,但这是我在早期版本的设备上找到的唯一解决方案。

我还以为你提出问题的整个意义就是要使其与早期版本的设备兼容。我的错误。

干杯


这个解决方案似乎是设置整个TextView的间距,而不是使用CharacterStyle / span设置文本子集的间距。 - alexbirkett
жҲ‘жҖҖз–‘ScaleXSpanзұ»дјјдәҺsetTextScaleXиҖҢдёҚжҳҜsetLetterSpacingгҖӮ - alexbirkett
我认为setLetterSpacing应该调整字距,而setTextScaleX会水平拉伸文本视图。 - Joao Sousa
如果你测试过这个,你会得到和我一样的最终结果。我甚至附上了截图来证明它。 - Joao Sousa
这种方法会破坏UTF-16代理对,并阻止正确地进行字形连字。 - kusma
显示剩余4条评论

1
你可以使用我用Kotlin编写的函数。它将允许你在一个TextView中修改两个字母之间的字距,而不会影响其他字母。
fun SpannableStringBuilder.setLetterSpacingBetweenTwoLettersSpans(
    firstLetter: Char,
    secondLetter: Char,
    spacing: Float
) {
    forEachIndexed { charIndex, startingChar ->
        if (startingChar == firstLetter && get(charIndex + 1) == secondLetter) {
            var negative = spacing < 0
            if (get(lastIndex) != ' ') append(' ')
            forEachIndexed { index, _ ->
                if (index > charIndex) {
                    val appliedSpacing = if (index == charIndex + 1) {
                        if (negative) -abs(spacing) else spacing
                    } else {
                        if (negative) spacing else -spacing
                    }
                    if (index != lastIndex) {
                        setSpan(object : MetricAffectingSpan() {
                            override fun updateMeasureState(textPaint: TextPaint) { apply(textPaint) }
                            override fun updateDrawState(tp: TextPaint?) { tp?.let { apply(tp) } }
                            private fun apply(paint: TextPaint) { paint.letterSpacing = appliedSpacing }
                        }, index, index + 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
                    }
                    negative = !negative
                }
            }
        }
    }
}

所以你可以像这样使用它:
val spannableStringBuilder = SpannableStringBuilder()
spannableStringBuilder.append("Bear")
spannableStringBuilder.setLetterSpacingBetweenTwoLettersSpans(
    firstLetter = "e",
    secondLetter = "a"
    spacing = 1f // Negative works aswell (that's why I initially did it)
)
yourTextView.text = spannableStringBuilder

我认为1f看起来就像在两个字母之间放了一个空格。负数也可以。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接