How to adjust text kerning in Android TextView?

2020-01-27 10:39发布

Is there a way to adjust the spacing between characters in an Android TextView? I believe this is typically called "kerning".

I'm aware of the android:textScaleX attribute, but that compresses the characters along with the spacing.

12条回答
我想做一个坏孩纸
2楼-- · 2020-01-27 10:52

AFAIK, you cannot adjust kerning in TextView. You may be able to adjust kerning if you draw the text on the Canvas yourself using the 2D graphics APIs.

查看更多
Luminary・发光体
3楼-- · 2020-01-27 10:55

One more solution.

public static SpannableStringBuilder getSpacedSpannable(Context context, String text, int dp) {
        if (text == null) return null;
        if (dp < 0) throw new RuntimeException("WRONG SPACING " + dp);
        Canvas canvas = new Canvas();
        Drawable drawable = ContextCompat.getDrawable(context, R.drawable.pixel_1dp);
        Bitmap main = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        canvas.setBitmap(main);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        SpannableStringBuilder builder = new SpannableStringBuilder();
        char[] array = text.toCharArray();
        Bitmap bitmap = Bitmap.createScaledBitmap(main, dp * main.getWidth(), main.getHeight(), false);
        for (char ch : array) {
            builder.append(ch);
            builder.append(" ");
            ImageSpan imageSpan = new ImageSpan(context, bitmap);
            builder.setSpan(imageSpan, builder.length() - 1, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        return builder;
    }

Where pixel_1dp is XML:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <solid android:color="@android:color/transparent"/>
    <size android:height="1dp" android:width="1dp"/>

</shape>

To set spacing use code like this:

textView.setText(getSpacedSpannable(context, textView.getText().toString(), <Your spacing DP>), TextView.BufferType.SPANNABLE);
查看更多
狗以群分
4楼-- · 2020-01-27 10:59

It's difficult to adjust spacing between characters, when you are using TextView. But if you can handle the drawing yourself, there should be some way to do that.

My answer to this question is: use your custom Span .

My code:

public class LetterSpacingSpan extends ReplacementSpan {
    private int letterSpacing = 0;

    public LetterSpacingSpan spacing(int space) {
        letterSpacing = space;

        return this;
    }


    @Override
    public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {
        return (int) paint.measureText(text, start, end) + (text.length() - 1) * letterSpacing;
    }


    @Override
    public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {
        int length = text.length();
        float currentX = x;

        for (int i = 1; i < length; i++) {          
            canvas.drawText(text, i, i + 1, currentX, y, paint);
            currentX += paint.measureText(text, i, i + 1) + letterSpacing;
         }
    }
}

Explain:

Building your own Span can help you achieve many amazing effect, like make a blur TextView, change the background or foreground for your TextView, even make some animation. I learn a lot from this post Span a powerful concept .

Because you are adding spacing to each character, so we should use a character level base span, in this case, ReplacementSpan is the best choice. I add a spacing method, so when using it, you can simply pass the space you want for each character as parameter.

When building your custom span, you need to override at least two method, getSize and draw. The getSize method should return the final width after we add the spacing for the whole charsequence, and inside the draw method block, you can control the Canvas to do the drawing you want.

So how we use this LetterSpacingSpan? It's easy:

Usage:

TextView textView;
Spannable content = new SpannableString("This is the content");
textView.setSpan(new LetterSpacingSpan(), 0, 4, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(content);

And that's it.

查看更多
Explosion°爆炸
5楼-- · 2020-01-27 11:02

Since Android 21, you can use set the letterSpacing attribute.

<TextView
    android:width="..."
    android:height="..."
    android:letterSpacing="1.3"/>
查看更多
我只想做你的唯一
6楼-- · 2020-01-27 11:04

The only way I found to adjust the kerning, is to create a custom font in which the glyph advance is altered.

查看更多
我想做一个坏孩纸
7楼-- · 2020-01-27 11:06

I wanted to use @PedroBarros answer, but by defining what the spacing should be in pixel.

Here's my edit to the applySpacing method :

private void applySpacing() {
    if (this == null || this.originalText == null) return;

    Paint testPaint = new Paint();
    testPaint.set(this.getPaint());
    float spaceOriginalSize = testPaint.measureText("\u00A0");
    float spaceScaleXFactor = ( spaceOriginalSize > 0 ? spacing/spaceOriginalSize : 1);

    StringBuilder builder = new StringBuilder();
    for(int i = 0; i < originalText.length(); i++) {
        builder.append(originalText.charAt(i));
        if(i+1 < originalText.length()) {
            builder.append("\u00A0");
        }
    }
    SpannableString finalText = new SpannableString(builder.toString());
    if(builder.toString().length() > 1) {
        for(int i = 1; i < builder.toString().length(); i+=2) {
            finalText.setSpan(new ScaleXSpan(spaceScaleXFactor), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
    }
    super.setText(finalText, BufferType.SPANNABLE);
}

I'm a beginner as an Android developer, please feel free to let me know if this is not good !

查看更多
登录 后发表回答