Frage

Gibt es eine Möglichkeit, den Abstand zwischen den Zeichen in einem Android TextView einstellen? Ich glaube, dies wird in der Regel als „Kerning“.

Ich bin mir bewusst, des android:textScaleX Attributs, aber das komprimiert die Zeichen zusammen mit dem Abstand.

War es hilfreich?

Lösung

AFAIK, können Sie nicht Kerning in TextView einzustellen. Sie können in der Lage sein, Kerning anpassen, wenn Sie den Text auf der Canvas zeichnen sich die 2D-Grafik-APIs.

Andere Tipps

Ich baute eine benutzerdefinierte Klasse, die Textview erweitert und fügt eine Methode „setSpacing“. Die Abhilfe ist ähnlich dem, was @Noah sagte. Das Verfahren fügt ein Leerzeichen zwischen den einzelnen Buchstaben des String und mit SpannedString Änderungen die TextScaleX der Räume, so dass positive und negative Abstände.

Hoffnung, dass jemand ^^

hilft
/**
 * Text view that allows changing the letter spacing of the text.
 * 
 * @author Pedro Barros (pedrobarros.dev at gmail.com)
 * @since May 7, 2013
 */

import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.ScaleXSpan;
import android.util.AttributeSet;
import android.widget.TextView;

public class LetterSpacingTextView extends TextView {

    private float spacing = Spacing.NORMAL;
    private CharSequence originalText = "";


    public LetterSpacingTextView(Context context) {
        super(context);
    }

    public LetterSpacingTextView(Context context, AttributeSet attrs){
        super(context, attrs);
    }

    public LetterSpacingTextView(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);
    }

    public float getSpacing() {
        return this.spacing;
    }

    public void setSpacing(float spacing) {
        this.spacing = spacing;
        applySpacing();
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        originalText = text;
        applySpacing();
    }

    @Override
    public CharSequence getText() {
        return originalText;
    }

    private void applySpacing() {
        if (this == null || this.originalText == null) return;
        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((spacing+1)/10), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            }
        }
        super.setText(finalText, BufferType.SPANNABLE);
    }

    public class Spacing {
        public final static float NORMAL = 0;
    }
}

es verwenden:

LetterSpacingTextView textView = new LetterSpacingTextView(context);
textView.setSpacing(10); //Or any float. To reset to normal, use 0 or LetterSpacingTextView.Spacing.NORMAL
textView.setText("My text");
//Add the textView in a layout, for instance:
((LinearLayout) findViewById(R.id.myLinearLayout)).addView(textView);

Wenn jemand eine einfache Möglichkeit suchen, das Kerning auf eine beliebige Zeichenfolge (technisch, CharSequence) anzuwenden, ohne eine TextView mit:

public static Spannable applyKerning(CharSequence src, float kerning)
{
    if (src == null) return null;
    final int srcLength = src.length();
    if (srcLength < 2) return src instanceof Spannable
                              ? (Spannable)src
                              : new SpannableString(src);

    final String nonBreakingSpace = "\u00A0";
    final SpannableStringBuilder builder = src instanceof SpannableStringBuilder
                                           ? (SpannableStringBuilder)src
                                           : new SpannableStringBuilder(src);
    for (int i = src.length() - 1; i >= 1; i--)
    {
        builder.insert(i, nonBreakingSpace);
        builder.setSpan(new ScaleXSpan(kerning), i, i + 1,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    return builder;
}

Hier ist meine Lösung, die (in Pixeln) zwischen den einzelnen Zeichen gleichmäßigen Abstand addiert. Diese Spanne übernimmt alle Text in einer einzigen Zeile ist. Diese im Grunde implementiert, was @commonsWare vermuten lässt.

SpannableStringBuilder builder = new SpannableStringBuilder("WIDE normal");
builder.setSpan(new TrackingSpan(20), 0, 4, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
...

private static class TrackingSpan extends ReplacementSpan {
    private float mTrackingPx;

    public TrackingSpan(float tracking) {
        mTrackingPx = tracking;
    }

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

    @Override
    public void draw(Canvas canvas, CharSequence text, 
        int start, int end, float x, int top, int y, 
        int bottom, Paint paint) {
        float dx = x;
        for (int i = start; i < end; i++) {
            canvas.drawText(text, i, i + 1, dx, y, paint);
            dx += paint.measureText(text, i, i + 1) + mTrackingPx;
        }
    }
}

Die einzige Art, wie ich das Kerning anpassen gefunden, ist eine eigene Schriftart zu erstellen, in dem die Glyphe vorher geändert wird.

Sie können auch versuchen, eine SpannedString aber Sie müßten analysieren sie und den Zeichenabstand für jedes der Wörter ändern

Diese Antwort kann für jemanden nützlich sein, den Text mit Kerning auf einer Leinwand zeichnen will, mit drawText (dies ist nicht über Text in einem Textview).

Da Lollipop ist das Verfahren setLetterSpacing auf Paint zur Verfügung. Wenn das SDK LUTSCHER und eingeschaltet ist, wird setLetterSpacing verwendet. Andernfalls wird eine Methode aufgerufen, dass etwas Ähnliches tut Vorschlag des @ dgmltn oben:

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        paint.setLetterSpacing(-0.04f);  // setLetterSpacing is only available from LOLLIPOP and on
        canvas.drawText(text, xOffset, yOffset, paint);
    } else {
        float spacePercentage = 0.05f;
        drawKernedText(canvas, text, xOffset, yOffset, paint, spacePercentage);
    }


/**
 * Programatically drawn kerned text by drawing the text string character by character with a space in between.
 * Return the width of the text.
 * If canvas is null, the text won't be drawn, but the width will still be returned
 * kernPercentage determines the space between each letter. If it's 0, there will be no space between letters.
 * Otherwise, there will be space between each letter. The  value is a fraction of the width of a blank space.
 */
private int drawKernedText(Canvas canvas, String text, float xOffset, float yOffset, Paint paint, float kernPercentage) {
    Rect textRect = new Rect();
    int width = 0;
    int space = Math.round(paint.measureText(" ") * kernPercentage);
    for (int i = 0; i < text.length(); i++) {
        if (canvas != null) {
            canvas.drawText(String.valueOf(text.charAt(i)), xOffset, yOffset, paint);
        }
        int charWidth;
        if (text.charAt(i) == ' ') {
            charWidth = Math.round(paint.measureText(String.valueOf(text.charAt(i)))) + space;
        } else {
            paint.getTextBounds(text, i, i + 1, textRect);
            charWidth = textRect.width() + space;
        }
        xOffset += charWidth;
        width += charWidth;
    }
    return width;
}

Es ist schwierig, Abstand zwischen den Zeichen einzustellen, wenn Sie Textview verwenden. Aber wenn man sich die Zeichnung umgehen kann, sollte es eine Möglichkeit geben, das zu tun.

Meine Antwort auf diese Frage lautet:. Ihre individuelle Span

Mein 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;
         }
    }
}

Erklären Sie:

Ihre eigene Span Gebäude können Sie viele erstaunliche Wirkung erzielen helfen, wie eine Unschärfe Textview machen, den Hintergrund oder Vordergrund für Ihre Textview ändern, auch einige Animation machen. Ich lerne viel von diesem Posten Span ein leistungsfähiges Konzept .

Weil Sie Abstand hinzufügen, um jedes Zeichen, so sollten wir eine Charakter-Level Basis Spanne, in diesem Fall benutzen, ist ReplacementSpan die beste Wahl. Ich füge eine spacing Methode, so dass, wenn es verwendet wird, können Sie einfach den Raum übergeben Sie für jedes Zeichen als Parameter werden sollen.

Wenn Ihre benutzerdefinierte Spanne Bau, benötigen Sie mindestens zwei Verfahren, getSize und draw außer Kraft zu setzen. Die getSize Methode sollte die endgültige Breite zurück, nachdem wir den Abstand für die ganze CharSequence, und im Inneren des draw Verfahrensblock hinzufügen, können Sie die Canvas steuern die Zeichnung, die Sie tun möchten.

So, wie wir diese LetterSpacingSpan benutzen? Es ist einfach:

Verwendung:

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

Und das ist es.

Es gibt eine kleine Bearbeiten von @Pedro Barros Antwort. Es ist nützlich, wenn Sie SpannableString verwenden, um es festgelegt, zum Beispiel wenn Sie verschiedene Farben von einigen Zeichen machen wollen:

private void applySpacing() {
    SpannableString finalText;

    if (!(originalText instanceof SpannableString)) {
        if (this.originalText == null) return;
        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");
            }
        }
        finalText = new SpannableString(builder.toString());
    } else {
        finalText = (SpannableString) originalText;
    }

    for (int i = 1; i < finalText.length(); i += 2) {
        finalText.setSpan(new ScaleXSpan((spacing + 1) / 10), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
    super.setText(finalText, TextView.BufferType.SPANNABLE);
}

Ich wollte @PedroBarros Antwort verwenden, aber durch die Definition, was der Abstand in Pixel sein sollte.

Hier ist meine Bearbeitung auf die applySpacing Methode:

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);
}

Ich bin ein Anfänger als Android-Entwickler, können Sie sich frei, mit mir zu informieren, wenn dies nicht gut ist!

Eine weitere Lösung.

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;
    }

Wo pixel_1dp ist 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>

Zur Einstellung verwenden Sie Code wie dieser Abstand:

textView.setText(getSpacedSpannable(context, textView.getText().toString(), <Your spacing DP>), TextView.BufferType.SPANNABLE);

Da Android 21, können Sie das letterSpacing Attribut verwenden.

<TextView
    android:width="..."
    android:height="..."
    android:letterSpacing="1.3"/>
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top