I have a TextView with maxlines=3 and I would like to use my own ellipsis, instead of
\"Lore ipsum ...\"
I need
\"Lore ips
Here is a solution for Kotlin.
The yourTextView.post{} is necessary because the textview won't be ellipsized until after it is rendered.
val customSuffix = "... [See more]"
yourTextView.post {
if (yourTextView.layout.getEllipsisStart(-1) != -1) {
val newText = yourTextView.text.removeRange(
yourTextView.layout.getEllipsisStart(-1) - customSuffix.length, yourTextView.text.length
)
yourTextView.text = String.format("%s%s", newText, customSuffix)
}
}
@George @jmhostalet i was doing this in my recycler view and it degraded the whole performance. `
ViewTreeObserver vto = previewContent.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
try {
Layout layout = previewContent.getLayout();
int index1 = layout.getLineStart(previewContent.getMaxLines());
if (index1 > 10 && index1 < ab.getPreviewContent().length()) {
String s =
previewContent.getText().toString().substring(0, index1 - 10);
previewContent
.setText(Html.fromHtml(
s + "<font color='#DC5530'>...और पढ़ें</font>"));
}
return true;
}catch (Exception e)
{
Crashlytics.logException(e);
}
return true;
}
});`
I've finally managed it in this way (may be not the best one):
private void setLabelAfterEllipsis(TextView textView, int labelId, int maxLines){
if(textView.getLayout().getEllipsisCount(maxLines-1)==0) {
return; // Nothing to do
}
int start = textView.getLayout().getLineStart(0);
int end = textView.getLayout().getLineEnd(textView.getLineCount() - 1);
String displayed = textView.getText().toString().substring(start, end);
int displayedWidth = getTextWidth(displayed, textView.getTextSize());
String strLabel = textView.getContext().getResources().getString(labelId);
String ellipsis = "...";
String suffix = ellipsis + strLabel;
int textWidth;
String newText = displayed;
textWidth = getTextWidth(newText + suffix, textView.getTextSize());
while(textWidth>displayedWidth){
newText = newText.substring(0, newText.length()-1).trim();
textWidth = getTextWidth(newText + suffix, textView.getTextSize());
}
textView.setText(newText + suffix);
}
private int getTextWidth(String text, float textSize){
Rect bounds = new Rect();
Paint paint = new Paint();
paint.setTextSize(textSize);
paint.getTextBounds(text, 0, text.length(), bounds);
int width = (int) Math.ceil( bounds.width());
return width;
}
I think the answer from @jmhostalet will degrade the performance (especially when dealing with lists and lots of TextViews) because the TextView draws the text more than once. I've created a custom TextView that solves this in the onMeasure()
and therefore only draws the text once.
I've originally posted my answer here: https://stackoverflow.com/a/52589927/1680301
And here's the link to the repo: https://github.com/TheCodeYard/EllipsizedTextView
Here's a nice way to do it with a Kotlin extension. Note that we need to wait for the view to layout before we can measure and append the suffix.
In TextViewExtensions.kt
fun TextView.setEllipsizedSuffix(maxLines: Int, suffix: String) {
addOnLayoutChangeListener(object: View.OnLayoutChangeListener {
override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) {
val allText = text.toString()
var newText = allText
val tvWidth = width
val textSize = textSize
if(!TextUtil.textHasEllipsized(newText, tvWidth, textSize, maxLines)) return
while (TextUtil.textHasEllipsized(newText, tvWidth, textSize, maxLines)) {
newText = newText.substring(0, newText.length - 1).trim()
}
//now replace the last few chars with the suffix if we can
val endIndex = newText.length - suffix.length - 1 //minus 1 just to make sure we have enough room
if(endIndex > 0) {
newText = "${newText.substring(0, endIndex).trim()}$suffix"
}
text = newText
removeOnLayoutChangeListener(this)
}
})
}
In TextUtil.kt
fun textHasEllipsized(text: String, tvWidth: Int, textSize: Float, maxLines: Int): Boolean {
val paint = Paint()
paint.textSize = textSize
val size = paint.measureText(text).toInt()
return size > tvWidth * maxLines
}
Then actually using it like this
myTextView.setEllipsizedSuffix(2, "...See more")
Note: if your text comes from a server and may have new line characters, then you can use this method to determine if the text has ellipsized.
fun textHasEllipsized(text: String, tvWidth: Int, textSize: Float, maxLines: Int): Boolean {
val paint = Paint()
paint.textSize = textSize
val size = paint.measureText(text).toInt()
val newLineChars = StringUtils.countMatches(text, "\n")
return size > tvWidth * maxLines || newLineChars >= maxLines
}
StringUtils
is from implementation 'org.apache.commons:commons-lang3:3.4'