The TextView.setLetterSpacing
allows the letter spacing / character spacing to be set.
Is there a corresponding CharacterStyle / span class that allows the letter spacing to be set on a subset of the text in a TextView
?
The TextView.setLetterSpacing
allows the letter spacing / character spacing to be set.
Is there a corresponding CharacterStyle / span class that allows the letter spacing to be set on a subset of the text in a TextView
?
So I solved this by wring my own span class, it works on API level 21 and up.
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);
}
}
You can take a look at this custom class implementation that implements what you need.
EDIT: Ok, if you want to apply to a small portion of the text you can do this:
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));
It's not perfect though.
EDIT:
To be clear, this is the outcome of the code above
The first line ("Custom Spannable") is the result of (+/-) the code above. The second line ("Letter Spacing") is the result of the property android:letterSpacing="1"
.
As you can see, and as I said, it's not perfect, but is the only solution I found for pre-lollipop devices.
I also thought the whole point of your question was to make this compatible with pre-lollipop devices. My bad.
Cheers
You can use this function that I have written in Kotlin. It will allow you to modify letter spacing between two letters in one TextView without other letters being affected.
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
}
}
}
}
}
So you can use it like this:
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 will look as if you'd put a space between the two letters I think. Negative will work aswell.