I have a TextView
storing an address that is two lines long. The street goes on the first line and is bold. The city & state go on the second line and are not bold. I want the end of each line ellipsized if the individual text on that line runs over. I'm using a SpannableString
to store the address because I want the street address to be bold and the city & state to be not bold.
Is there a way to do this that isn't a total hack without using two TextViews
for each line?
Example outputs:
Ex:
123812 Washington A...
Schenectady New Yor...
Ex:
2792 Dantzler Boulev...
Charleston SC, 29406
Ex:
3 Main Street
Atlanta GA
I had the same problem and solved it by creating an EllipsizeLineSpan class. You can wrap each line that you want to ellipsize with it.
Example for marking up a spannable string with it:
SpannableStringBuilder textspan = new SpannableStringBuilder("#1.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"+
"Protect from ellipsizing #2.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n"+
"#3.Lorem ipsum dolor sit amet, consectetur adipisicing elit\n"+
"#4.Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.\n");
// find ellipsizable text (from '#' to newline)
Pattern pattern = Pattern.compile("#.*\\n", Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(textspan);
while(matcher.find()) {
textspan.setSpan(new EllipsizeLineSpan(), matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
EllipsizeLineSpan:
public class EllipsizeLineSpan extends ReplacementSpan implements LineBackgroundSpan {
int layoutLeft = 0;
int layoutRight = 0;
public EllipsizeLineSpan () {
}
@Override
public void drawBackground (Canvas c, Paint p,
int left, int right,
int top, int baseline, int bottom,
CharSequence text, int start, int end,
int lnum) {
Rect clipRect = new Rect();
c.getClipBounds(clipRect);
layoutLeft = clipRect.left;
layoutRight = clipRect.right;
}
@Override
public int getSize (Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fm) {
return layoutRight - layoutLeft;
}
@Override
public void draw (Canvas canvas, CharSequence text, int start, int end,
float x, int top, int y, int bottom, Paint paint) {
float textWidth = paint.measureText(text, start, end);
if (x + (int) Math.ceil(textWidth) < layoutRight) { //text fits
canvas.drawText(text, start, end, x, y, paint);
} else {
float ellipsiswid = paint.measureText("\u2026");
// move 'end' to the ellipsis point
end = start + paint.breakText(text, start, end, true, layoutRight - x - ellipsiswid, null);
canvas.drawText(text, start, end, x, y, paint);
canvas.drawText("\u2026", x + paint.measureText(text, start, end), y, paint);
}
}
}
DangVarmit's answer helped me a lot but after using it for a while, I found out that sometimes the top offset of the text is randomly incorrect. Here is the part that needs to be replaced:
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fontMetricsInt: Paint.FontMetricsInt?): Int {
fontMetricsInt?.let {
it.top = paint.getFontMetricsInt(it)
}
return Math.round(paint.measureText(text, start, start))
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With