سؤال

هل هناك طريقة لضبط التباعد بين الأحرف في Android TextView؟أعتقد أن هذا يسمى عادةً "تقنين الأحرف".

أنا على علم ب android:textScaleX السمة، ولكن هذا يضغط الأحرف مع التباعد.

هل كانت مفيدة؟

المحلول

وAFAIK، لا يمكنك ضبط المسافات بين الحروف في TextView. قد تكون قادرة على ضبط المسافات بين الحروف إذا كنت رسم النص على Canvas نفسك باستخدام واجهات برمجة التطبيقات الرسومات 2D.

نصائح أخرى

ولقد بنيت فئة مخصصة الذي يمتد TextView ويضيف طريقة "setSpacing". الحل يشبه ما قالهNoah. ويضيف طريقة مسافة بين كل حرف من سلسلة ومع التغييرات SpannedString وTextScaleX من المساحات، مما يسمح للتباعد الإيجابية والسلبية.

وعلى أمل أن يساعد شخص ^^

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

وعن طريق ما يلي:

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

وإذا كان أي شخص يبحث عن وسيلة بسيطة لتطبيق المسافات بين الحروف إلى أي سلسلة (من الناحية الفنية، CharSequence) دون استخدام TextView:

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

وهنا حل بي، وهو ما يضيف تباعد موحد (بالبكسل) بين كل حرف. هذه الفترة يتحمل كامل النص في سطر واحد. هذا يطبق أساسا ما يشيرcommonsWare.

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

والطريقة الوحيدة التي وجدت لضبط المسافات بين الحروف، هو إنشاء خط مخصص الذي تم تغييره تقدم الصورة الرمزية.

ويمكنك أيضا حاول استخدام SpannedString ولكن قد تحتاج إلى تحليل وتغيير تباعد الأحرف لكل من الكلمات

ويمكن أن تكون هذه الإجابة مفيدة للشخص الذي يريد أن رسم النص مع تقليص المسافات على قماش، وذلك باستخدام drawText (وهذا ليس عن النص في TextView).

ومنذ المصاصة، وsetLetterSpacing طريقة متاح في الطلاء. إذا كان SDK هو LOLLIPOP وعلى، ويستخدم setLetterSpacing. خلاف ذلك، يتم استدعاء الأسلوب الذي يفعل شيئا من هذا القبيل لاقتراح @ dgmltn في أعلاه:

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

من الصعب ضبط التباعد بين الأحرف عند استخدام TextView.ولكن إذا كنت تستطيع التعامل مع الرسم بنفسك، فيجب أن تكون هناك طريقة ما للقيام بذلك.

جوابي على هذا السؤال هو: استخدم النطاق المخصص الخاص بك .

رمز بلدي:

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

يشرح:

بناء بنفسك Span يمكن أن تساعدك في تحقيق العديد من التأثيرات المذهلة، مثل جعل TextView ضبابيًا، أو تغيير الخلفية أو المقدمة لـ TextView الخاص بك، أو حتى إنشاء بعض الرسوم المتحركة.أتعلم الكثير من هذا المنصب تمتد لمفهوم قوي .

نظرًا لأنك تقوم بإضافة مسافات إلى كل حرف، فيجب علينا استخدام نطاق أساسي لمستوى الحرف، وفي هذه الحالة، يعد استبدال Span هو الخيار الأفضل.أقوم بإضافة أ spacing الطريقة، لذلك عند استخدامها، يمكنك ببساطة تمرير المساحة التي تريدها لكل حرف كمعلمة.

عند إنشاء نطاقك المخصص، تحتاج إلى تجاوز طريقتين على الأقل، getSize و draw.ال getSize يجب أن تقوم الطريقة بإرجاع العرض النهائي بعد أن نضيف التباعد للتسلسل الكامل، وداخل draw كتلة الطريقة، يمكنك التحكم في Canvas للقيام بالرسم الذي تريده.

إذًا كيف نستخدم LetterSpacingSpan؟من السهل:

الاستخدام:

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

وهذا كل شيء.

وهناك تحرير صغيرة منPedro باروس الجواب. ومن المفيد إذا كنت تستخدم SpannableString إلى تعيينها، على سبيل المثال، إذا كنت تريد أن تجعل ألوان مختلفة من بعض الشخصيات:

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

وكنت أرغب في استخدامPedroBarros الجواب، ولكن عن طريق تحديد ما ينبغي أن يكون التباعد في بكسل.

وهنا بلدي التحرير إلى طريقة applySpacing:

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

وأنا مبتدئين ومطور الروبوت، لا تتردد في اسمحوا لي أن أعرف إذا كان هذا ليس جيدا!

وحل واحد أكثر من ذلك.

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

وأين pixel_1dp هو 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>

لتعيين تباعد كود استخدام مثل هذا:

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

ومنذ الروبوت 21، يمكنك استخدام تعيين السمة letterSpacing.

<TextView
    android:width="..."
    android:height="..."
    android:letterSpacing="1.3"/>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top