2009-10-28 43 views

Respuesta

11

AFAIK, no se puede ajustar el interletraje en TextView. Puede ajustar el interletraje si dibuja el texto en el Canvas usando las API gráficas 2D.

+0

Bueno, supongo que eso es todo entonces. Cheers, Mike – emmby

+1

Hola, @CommonsWare, ¿Cómo ajusto el interletraje si dibujo texto en el lienzo usando las API de gráficos 2D? ¿Me darías una pista? – neevek

+0

Puede implementar un ajuste de interletraje suministrando su propia versión modificada de la fuente utilizada. – mvds

1

También puede intentar usar un SpannedString pero que tendría que analizar y cambiar el espaciado entre caracteres para cada una de las palabras

3

La única manera que encontré para ajustar el perfil fuera de línea, es la creación de una fuente personalizada en la que el avance del glifo está alterado.

60

Creé una clase personalizada que amplía TextView y agrega un método "setSpacing". La solución es similar a lo que dijo @Noah. El método agrega un espacio entre cada letra del String y con SpannedString cambia el TextScaleX de los espacios, lo que permite el espaciado positivo y negativo.

la esperanza de que ayude a alguien ^^

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

Su uso:

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

Esto es genial! Gracias por esto, realmente me ha ayudado –

+0

Este código funciona bien para reducir el espacio entre letras, pero desafortunadamente rompe el envoltorio de texto para mí. –

+5

Reemplazando builder.append ("") con no-break space builder.append ("\ u00A0") ha resuelto el problema. Gracias por el código! –

13

Si alguien está buscando una forma sencilla de aplicar el ajuste entre caracteres en cualquier cadena (técnicamente, CharSequence) sin necesidad de utilizar una 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; 
} 
3

aquí está mi solución , que agrega espacio uniforme (en píxeles) entre cada personaje. Este lapso supone que todo el texto está en una sola línea. Esto básicamente implementa lo que sugiere @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; 
     } 
    } 
} 
0

Hay una pequeña edición de la respuesta de @Pedro Barros. Es útil si usa SpannableString para establecerlo, p. si quieres hacer diferentes colores de algunos personajes:

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

que quería utilizar @PedroBarros respuesta, pero definiendo lo que el espacio debe estar en píxeles.

Aquí está mi edición en el método 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); 
} 

Soy un principiante como un desarrollador de Android, por favor no dude en hacérmelo saber si esto no es bueno!

1

Esta respuesta puede ser útil para alguien que quiere dibujar texto con interletraje en un lienzo, utilizando drawText (esto no se trata de texto en un TextView).

Desde Lollipop, el método setLetterSpacing está disponible en Paint. Si el SDK es LOLLIPOP y está activado, se usa setLetterSpacing. De lo contrario, se invoca un método que hace algo similar a la sugerencia de @ dgmltn arriba:

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

Uno más solución.

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

Dónde pixel_1dp es 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> 

Para establecer el espaciado utilice el código de la siguiente manera:

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