The issue I'm trying to fix is the following: I'm having a TextView
and I'm using a Spannable
to set some characters bold.
The text needs to have a maxim of 2 lines ( android:maxLines="2"
) and I want the text to be ellipsized, but for some reason I cannot make the text ellipsized.
Here is the simple code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:maxLines="2"
android:ellipsize="end"
android:bufferType="spannable"
android:text="@string/app_name"
android:textSize="15dp"/>
</LinearLayout>
and the activity:
public class MyActivity extends Activity {
private TextView name;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name= (TextView) findViewById(R.id.name);
name.setText("Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy ");
Spannable spannable = (Spannable)name.getText();
StyleSpan boldSpan = new StyleSpan( Typeface.BOLD );
spannable.setSpan( boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE );
}
}
The text is truncated, no "..." are displayed.
Android Ellipsize Android TextView ellipsize property Causes words in the text that are longer than the view's width to be ellipsized ( means to shorten text using an ellipsis, i.e. three dots …) instead of broken in the middle to fit it inside the given view.
Set The Text of The TextView You can set the text to be displayed in the TextView either when declaring it in your layout file, or by using its setText() method. The text is set via the android:text attribute. You can either set the text as attribute value directly, or reference a text defined in the strings.
for the new line in TextView just add \n in middle of your text it works..
If you want a horizontal scrollable text in your app, use android:ellipsize="marquee" where a single line large text will be scrolling.
I realise this is a very old post, but seeing as it's still unanswered and I also ran into this issue today I thought I would post a solution to this. Hopefully it helps someone in the future.
ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
@Override
public void onGlobalLayout()
{
ViewTreeObserver viewTreeObserver = textView.getViewTreeObserver();
viewTreeObserver.removeOnGlobalLayoutListener(this);
if (textView.getLineCount() > 5)
{
int endOfLastLine = textView.getLayout().getLineEnd(4);
String newVal = textView.getText().subSequence(0, endOfLastLine - 3) + "...";
textView.setText(newVal);
}
}
});
Having same problem and seems the following works for me:
Spannable wordtoSpan = new SpannableString(lorem);
wordtoSpan.setSpan(new ForegroundColorSpan(0xffff0000), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
wordtoSpan.setSpan(new ForegroundColorSpan(0xff00ffff), 20, 35, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(wordtoSpan);
in xml the textView has android:multiLine
set, and android:ellipsize="end"
, and android:singleLine="false
;
You're right in that ellipsize, declared either in xml or in code won't work on spannable text.
However, with a little bit of investigation you can actually do the ellipsizing yourself:
private TextView name;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
name= (TextView) findViewById(R.id.name);
String lorem = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy "
name.setText(lorem);
Spannable spannable = (Spannable)name.getText();
StyleSpan boldSpan = new StyleSpan(Typeface.BOLD);
spannable.setSpan( boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
int maxLines = 2;
// in my experience, this needs to be called in code, your mileage may vary.
name.setMaxLines(maxLines);
// check line count.. this will actually be > than the # of visible lines
// if it is long enough to be truncated
if (name.getLineCount() > maxLines){
// this returns _1 past_ the index of the last character shown
// on the indicated line. the lines are zero indexed, so the last
// valid line is maxLines -1;
int lastCharShown = name.getLayout().getLineVisibleEnd(maxLines - 1);
// chop off some characters. this value is arbitrary, i chose 3 just
// to be conservative.
int numCharsToChop = 3;
String truncatedText = lorem.substring(0, lastCharShown - numCharsToChop);
// ellipsize! note ellipsis character.
name.setText(truncatedText+"…");
// reapply the span, since the text has been changed.
spannable.setSpan(boldSpan, 10, 15, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
This may a little trick by using reflection to solve this problem. After reading the source code of AOSP, in TextView.java, DynamicLayout only contains a static field member named sStaticLayout and it's constructed by new StaticLayout(null) without any params including maxLines.
Therefore, doEllipsis will alway be false as mMaximumVisibleLineCount is set Integer.MAX_VALUE by default.
boolean firstLine = (j == 0);
boolean currentLineIsTheLastVisibleOne = (j + 1 == mMaximumVisibleLineCount);
boolean lastLine = currentLineIsTheLastVisibleOne || (end == bufEnd);
......
if (ellipsize != null) {
// If there is only one line, then do any type of ellipsis except when it is MARQUEE
// if there are multiple lines, just allow END ellipsis on the last line
boolean forceEllipsis = moreChars && (mLineCount + 1 == mMaximumVisibleLineCount);
boolean doEllipsis =
(((mMaximumVisibleLineCount == 1 && moreChars) || (firstLine && !moreChars)) &&
ellipsize != TextUtils.TruncateAt.MARQUEE) ||
(!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) &&
ellipsize == TextUtils.TruncateAt.END);
if (doEllipsis) {
calculateEllipsis(start, end, widths, widthStart,
ellipsisWidth, ellipsize, j,
textWidth, paint, forceEllipsis);
}
}
So I extends the TextView and make a View named EllipsizeTextView
public class EllipsizeTextView extends TextView {
public EllipsizeTextView(Context context) {
super(context);
}
public EllipsizeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
public EllipsizeTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
StaticLayout layout = null;
Field field = null;
try {
Field staticField = DynamicLayout.class.getDeclaredField("sStaticLayout");
staticField.setAccessible(true);
layout = (StaticLayout) staticField.get(DynamicLayout.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (layout != null) {
try {
field = StaticLayout.class.getDeclaredField("mMaximumVisibleLineCount");
field.setAccessible(true);
field.setInt(layout, getMaxLines());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (layout != null && field != null) {
try {
field.setInt(layout, Integer.MAX_VALUE);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
problem solved!
Another solution is to overwrite onDraw
of TextView
. The following suggested solution, doesn't make use any reflection technique. Hence, shouldn't break in the future, in case any member variable naming changes.
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
public class EllipsizeTextView extends TextView {
private static final String THREE_DOTS = "...";
private static final int THREE_DOTS_LENGTH = THREE_DOTS.length();
private volatile boolean enableEllipsizeWorkaround = false;
private SpannableStringBuilder spannableStringBuilder;
public EllipsizeTextView(Context context) {
super(context);
}
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public EllipsizeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public void setEnableEllipsizeWorkaround(boolean enableEllipsizeWorkaround) {
this.enableEllipsizeWorkaround = enableEllipsizeWorkaround;
}
// https://stackoverflow.com/questions/14691511/textview-using-spannable-ellipsize-doesnt-work
// https://blog.csdn.net/htyxz8802/article/details/50387950
@Override
protected void onDraw(Canvas canvas) {
if (enableEllipsizeWorkaround && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
final Layout layout = getLayout();
if (layout.getLineCount() >= getMaxLines()) {
CharSequence charSequence = getText();
int lastCharDown = layout.getLineVisibleEnd(getMaxLines()-1);
if (lastCharDown >= THREE_DOTS_LENGTH && charSequence.length() > lastCharDown) {
if (spannableStringBuilder == null) {
spannableStringBuilder = new SpannableStringBuilder();
} else {
spannableStringBuilder.clear();
}
spannableStringBuilder.append(charSequence.subSequence(0, lastCharDown - THREE_DOTS_LENGTH)).append(THREE_DOTS);
setText(spannableStringBuilder);
}
}
}
super.onDraw(canvas);
}
}
This is a known issue in the Android framework: https://code.google.com/p/android/issues/detail?id=67186
A simple and working solution
This is my code ->
<TextView
android:id="@+id/textViewProfileContent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="false"
android:ellipsize="end"
android:maxLines="3"
android:textSize="14sp"
android:textColor="#000000" />
SpannableStringBuilder sb = new SpannableStringBuilder();
SpannableString attrAdditional = new SpannableString(additionalText);
attrAdditional.SetSpan(new StyleSpan(TypefaceStyle.Bold), 0, additionalText.Length, 0);...
sb.Append(attrAdditional);...
ProfileContent.SetText(sb, **TextView.BufferType.Normal**);
Result
As for single line case, android:maxLines is not working,but android:singleLine is ok.
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